-calculus

29
-calculus

description

-calculus. C. Functionele talen. basisidee: programmeren = functies bouwen Het uitrekenen van expressies gebeurt door substitutie. een programma definieert een functie (input  output) samenstellen van complexer functies uit eenvoudiger delen: opbouwen van functionele termen - PowerPoint PPT Presentation

Transcript of -calculus

Page 1: -calculus

-calculus

Page 2: -calculus

C. Functionele talen

• basisidee: programmeren = functies bouwen

• Het uitrekenen van expressies gebeurt door substitutie

• een programma definieert een functie (input output)

• samenstellen van complexer functies uit eenvoudiger

delen: opbouwen van functionele termen

• eenvoudige datastructuren: lijsten

(2 + 3) * (23 - 17)

5 * (23 - 17)

5 * 6

30Niet meer verder te “reduceren”:normaalvorm

Page 3: -calculus

Haskell: quicksort

f [] = []

f (x:xs) = f ys ++ [x] ++ f zs

where

ys = [a | a xs, a x]

zs = [b | b xs, b > x]

3

Bevat variabelen, lijsten, operators, pattern matching, list comprehension, …

Page 4: -calculus

-calculus

• Ingevoerd door Alonzo Church en Stephen Kleene, begin 30-er jaren, als algemeen berekeningsmodel

• In 1936 gebruikt om een negatief antwoord te geven op het Entscheidungsprobleem:

Equivalentie van willekeurige -expressies (equivalent = zelfde functie) is onbeslisbaar

• Formele basis voor functioneel programmeren: de mechanismen van “echte” functionele talen, zoals Haskell, zijn er “bovenop” gebouwd

Page 5: -calculus

-calculus

• Basis voor denotationele semantiek van programmeertalen (Scott - Strachey): definieer de precieze betekenis (semantiek) van programma’s en programmadelen door middel van een partiële functie die het input-output gedrag beschrijft.

• Naïeve, ongetypeerde -calculus leidt tot inconsistenties, net zoals de naïeve verzamelingenleer. Dat werd opgelost door de getypeerde -calculus, waarbij elke term een type krijgt. Dit legde de basis voor de studie van typesystemen in het algemeen.

Page 6: -calculus

-calculus

• Syntax: variabelen, -abstractie, functie-applicatie

• De fundamentele stap: - reductie

• Gebonden en vrije variabelen, - conversie

• Combinators, gesloten termen

• Booleans in de - calculus

• Integers in de - calculus

• Recursie, de fixpunt - combinator Y

• Reductiestrategieën, normal order en applicative order

Page 7: -calculus

Syntax

De syntax van een -expressie is extreem eenvoudig: er zijn enkel namen (variabelen), functies (abstracties) en applicaties.

< expression > ::= < name > | < function > | < application >

< name > ::= x | y | z | ...

< function > ::= < name > . < body >

< body > ::= < expression >

< application > ::= ( < expression > < expression > )

Page 8: -calculus

Expressies

De algemene idee is dat een < function> een functiedefinitie voorstelt, met < name > als (enige) formele parameter, en dat in een < application > een functie wordt toegepast op een argument.

voorbeelden:

x.x (de identiteitsfunctie )

fun.arg.(fun arg) (apply)

x.y.y (select second, false)

y.(x.(y (x x)) x.(y (x x))) (Y combinator)

Page 9: -calculus

Opmerkingen

• Strict genomen moeten we ook constanten invoeren.Formeel kunnen we ze behandelen als namen die niet onder een mogen voorkomen. Voorlopig laten we constanten gewoon buiten beschouwing.• Elke expressie (-term) stelt een functie voor.

Page 10: -calculus

Opmerkingen

• Elke expressie (-term) stelt een functie voor.• De kijk op functies is extensioneel: twee functies worden beschouwd als gelijk als ze dezelfde extensie hebben, m.a.w. als ze voor elk argument dezelfde waarde geven. Over de manier waarop die waarde berekend wordt, dus met welk algoritme, zegt dat niets.In de context van programmatransformaties speekt men ook van semantische equivalentie.• De vraag of twee termen (extensioneel) met dezeldde functie overeenkomen, is onbeslisbaar (cfr. equivalentieprobleem voor Turing machines).

Page 11: -calculus

- reductie

Idee: applicatie van functie x. <body> op

<argument-expressie> =

De variabele x speelt dus de rol van formele parameter.

Een -expressie wordt uitgewerkt (geëvalueerd) door dit

herhaaldelijk te doen, tot er niets meer uit te werken valt:

dus tot er geen "redex" (<name>. <body> <arg>) meer

te vinden is.

expressie bekomen door in <body> de naam x te vervangen door <argument-expressie>.

Page 12: -calculus

- converteerbaarheid

Een expressie M is -converteerbaar (gelijk) aan een expressie N, N = M, als N -congruent (zie verder) is met M, of M N, of N M, of er is een -expressie E zodat M = E en E = N.

De zo verkregen relatie = is uiteraard een equivalentierelatie.

Page 13: -calculus

- reductie: voorbeelden

( (x.y.x z.z) t) ( y.z.z t ) z.z (select first)

(x.( (f. arg.(f arg) z.z) x) t) ( (f. arg.(f arg) z.z) t) ( arg.(z.z arg) t) ( z.z t) t (identity_2)

Normal order: eerst de functiedefinitie uitwerken

Page 14: -calculus

Opmerkingen

• Er is dus in principe maar één reductieregel nodig!• Expressies worden uitgewerkt door deze reductieregel toe te passen tot er geen geschikt deel van de vorm (x.<body> <argument>) meer gevonden kan worden; de expressie is dan in normaalvorm gebracht.• Je zou kunnen verwachten dat het resultaat van een -reductie korter is dan de expressie waarvan je vertrekt, zoals in

( y.( z.w y ) ( x.(x x) x.(x x) ) ) ( z.w ( x.(x x) x.(x x) ) ) w

Page 15: -calculus

maar dat hoeft niet zo te zijn:

( x.(x x) x.(x x) ) ( x.(x x) x.(x x) ) ( x.(x x) x.(x x) ) …

Er zijn dus oneindige reducties mogelijk.

We willen dat twee expressies die enkel verschillen doordat de namen na de verschillen, als gelijk beschouwd worden.

Page 16: -calculus

-calculus en concurrency

Het zal blijken dat de volgorde waarin delen van een term (redexes) vervangen worden, er niet toe doet. I.h.b. kunnen verschillende, disjuncte delen van een term dus tegelijk, onafhankelijk van elkaar vervangen worden. Dit berekeningsmodel is daarom geschikt voor het invoeren van parallellisme.

Het is mogelijk speciale hardware te bouwen om dit exploiteren, en dat is ook gebeurd (LISP - machine).

Page 17: -calculus

- conversie: het probleem

Bekijk de functie apply == fun.arg.(fun arg). Bedoeling is dat die het eerste argument toepast op het tweede. De namen van de "formele parameters" fun en arg hebben geen belang, dus ook x.y.(x y) stelt apply voor. Maar:

( (apply arg) z) == ( (fun.arg.(fun arg) arg) z)

(arg.(arg arg) z)

(z z) wat niet de bedoeling is

Page 18: -calculus

wel goed gaat:

( (apply arg) z) == ( (x.y.(x y) arg) z)

(y.(arg y) z)

(arg z) .

De reden waarom het bij ( (fun.arg.(fun arg) arg) z) fout

gaat is dat in de eerste stap, waarin arg (de "actuele parameter") gesubstitueerd wordt voor fun, die arg verward wordt met de

arg van arg. Als we apply definiëren als x.y.(x y) doet die moeilijkheid zich niet voor. Besluit: we moeten, voor we een

functie toepassen, de "formele parameters" eerst vervangen

door nieuwe, onschadelijke namen. Deze operatie noemt men een -conversie.

Page 19: -calculus

Vrije en gebonden namen

Op een bepaalde plaats in een expressie is een naam x gebonden als hij daar voorkomt in een subterm van de vorm x.P. Anders is x daar vrij.

y.(x.(x y) y.(y x)) gebonden

((x y.(y (x y.(y x.(y x))))) x) vrij

x. ((x y.(y (x y.(y x.(y x))))) x)

Page 20: -calculus

- congruentie

-regel:

x.E z.{z/x}E, voor elke z die niet voorkomt in E.

{z/x}E stelt hier het resultaat voor van de substitutie van z voor x in E.

-congruentie:

Twee expressies M en N zijn -congruent als ofwel M N, of M N, of N wordt verkregen uit M door een subexpressie S ervan te vervangen door een expressie T zo dat S T, of er is een expressie R zo dat M -congruent is met R en R is -congruent met N.

Page 21: -calculus

Reductie, met - conversie

Bij het uitwerken van een functie-applicatie (x.P Q):voor elke naam y die vrij voorkomt in Q, en die gebonden voorkomt in P, vervang voor het uitwerken de gebonden occurrences van y overal in P door een nieuwe naam y'. bv. in ( (fun.arg.(fun arg) arg) z) :

vervang arg door een nieuwe, "ongevaarlijke" naam.

Resultaat: ( (fun.arg'.(fun arg') arg) z)

Page 22: -calculus

Rekenen in de - calculus: combinators

Een combinator is een -expressie met enkel gebonden variabelen. Het gedrag van een combinator is daarom onafhankelijk van de context waarin hij optreedt. Constanten (True, 5, AND, +, ...) kunnen voorgesteld worden door combinatoren.

x.y.x select_first

e1. e2. c.((c e1) e2) cond

Page 23: -calculus

Booleans

true == x.y.x select_first

false == x.y.y select_second

cond == e1. e2. c.((c e1) e2) toegepast op <exp1> en

<exp2> geeft:

wat, toegepast op true == x.y.x geeft:

((e1. e2. c.((c e1) e2) <exp1>) <exp2>) (e2. c.((c <exp1>) e2) <exp2>)

c.((c <exp1>) <exp2>)

(c.((c <exp1>) <exp2>) x.y.x) ((x.y.x <exp1>) <exp2>)

(y.<exp1> <exp2>) <exp1>

Page 24: -calculus

Booleans

en analoog leidt een toepassing op false tot <exp2>.

not kan nu gedefinieerd worden als:

not == x.((cond false) true) x)

want als het derde argument van cond (hier x dus) true is, selecteert cond het eerste van zijn twee argumenten, dus false. Analoog, als het derde argument false is, selecteert het true.

x.(((cond false) true) x) kan vereenvoudigd worden:

en dus kunnen we gebruiken: not == x.((x false) true)

(((e1. e2. c.((c e1) e2) false) true) x) ((e2. c.((c false) e2) false) true) x) (c.((c false) true) x)

((x false) true)

Page 25: -calculus

AND

de functie X and Y kan gedefinieerd worden als

and == x.y.(((cond y) false) x) want als x true is, dan neemt men de waarde van y over, en als x false is, dan selecteert men false. Het deel (((cond y) false) x) kan weer vereenvoudigd worden, nu tot ((x y) false).

We gebruiken dus:

and == x.y. ((x y) false) (en or == x.y. ((x true) y) ).bv. voor true and false: ((and true) false)

== ((x.y. ((x y) false) true) false) (y. ((true y) false) false) ((true false) false)

... false

Page 26: -calculus

Gehele getallen: Church numerals

We definieren combinatoren die 0,1,2, ... voorstellen:

0 == f.x.x 1 == f.x.(f x) 2 == f.x.(f (f x)) 3 == f.x.(f (f (f x)))

enzovoort. De successor-functie kan dan worden:

succ == n.f.x.(f ((n f) x))

bv: (succ 1) == (n.f.x.(f ((n f) x)) f.x.(f x)) f.x.(f ((f.x.(f x) f) x)) f.x.(f (x.(f x) x)) f.x.(f (f x)) == 2

Page 27: -calculus

Andere bewerkingen

plus == m.n.f.x.((m f) ((n f) x))

mult == m.n.f.(m (n f))

Page 28: -calculus

Andere bewerkingen

plus == m.n.f.x.((m f) ((n f) x))

mult == m.n.f.(m (n f))

sommige zijn behoorlijk complex - ook Church zelf raakte ervan overtuigd dat de predecessor - functie niet voorgesteld kon worden, tot Kleene vond:

pred ==n.(((n p.z.((z (succ (p true))) (p true))) z.((z 0) 0)) false)

In gewone notatie:pred(n) = n -1, als n > 0 0, als n = 0

Page 29: -calculus

predecessor - vervolg

Deze definitie is gebaseerd op het volgende.

z.((z a) b) stelt het geordende paar [a,b] voor, en heeft de eigenschap

(z.((z a) b) true) a en (z.((z a) b) ) false) b

Dus stelt

next == p. z.((z (succ (p true))) (p true))

de functie voor die [n+1,n] berekent uit [n,n-1]: als we voor p het paar [n,n-1] invullen, selecteert (p true) daaruit het eerste element n, en vervolgens vormt men een paar met (succ (p true)) en (p true). De predecessor-functie verkrijgt men door next n keer toe te passen, vertrekkend van het paar [0,0]:

z.((z 0) 0)

en dan het tweede element te selecteren door de verkregen functie op false toe te passen.