We are currently studying Lambda Calculus and have begun Beta Reduction.
Our lecturer is using some notation that was not properly explained to us.
Below is what we have been given.
𝛽-reduction:
(λ v . e₁) e₂
⤳𝛽
[e₂/v]e₁ where [e₂/v] is an operator that replaces each v with an e₂
Definition of operator [e/v]
[e/v]v = e
[e/v₂]v₁ = v₁ (when v₁≠v₂, i.e., different identifiers)
[e/v](e₁ e₂) = [e/v]e₁ [e/v]e₂
[e₂/v](λ v . e₁) = (λ v . e₁)
[e₂/v₂](λ v₁ . e₁) = (λ v₁ . [e₂/v₂]e₁) (when v₁∉FV(e₂))
[x/v] (λ x . f v)
⤳ λ x . f x (WRONG, x∈FV(x)={x})
I have tried to find resources online but none seem to use the same notation as him with the '/' sign.
Any explanation of the above code would be very much appreciated.
Related
Let a pure λ function be a term with nothing but abstractions and applications. On JavaScript, it is possible to infer the source code of a pure function by applying all abstractions to variadic functions that collect their argument list. That is, this is possible:
lambdaSource(function(x){return x(x)}) == "λx.(x x)"
See the code for lambdaSource on this gist. That function became particularly useful for my interests since it allows me to use existing JS engines to normalize untyped lambda calculus expressions much faster than any naive evaluator I could code by myself. Moreover, I know λ-calculus functions can be expressed in Haskell with help of unsafeCoerce:
(let (#) = unsafeCoerce in (\ f x -> (f # (f # (f # x)))))
I do not know how to implement lambdaSource in Haskell because of the lack of variadic functions. Is it possible to infer the normalized source of a pure λ function on Haskell, such that:
lambdaSource (\ f x -> f # (f # (f # x))) == "λ f x . f (f (f x))"
?
Yes, you can, but you need to provide the spine of the type of your function, so it doesn't work for ULC. See also the whole lecture notes.
But as Daniel Wagner says you can just use HOAS.
There is also another opportunity: here is something that looks like HOAS, but is FOAS actually, and all you need is suitable normalization by evaluation (in terms of quote, in terms of reify & reflect). pigworker also wrote a Haskell version of the Jigger, but I can't find it.
We can also do this type-safely in type theory: one way is to use liftable terms (which requires a postulate), or we can reify lambda terms into their PHOAS representation and then convert PHOAS to FOAS (which is very complicated).
EDIT
Here is some HOAS-related code:
{-# LANGUAGE GADTs, FlexibleInstances #-}
infixl 5 #
data Term a = Pure a | Lam (Term a -> Term a) | App (Term a) (Term a)
(#) :: Term a -> Term a -> Term a
Lam f # x = f x
f # x = App f x
instance Show (Term String) where
show = go names where
names = map (:[]) ['a'..'z'] ++ map (++ ['\'']) names
go :: [String] -> Term String -> String
go ns (Pure n) = n
go (n:ns) (Lam f) = concat ["\\", n, " -> ", go ns (f (Pure n))]
go ns (App f x) = concat [go ns f, "(", go ns x, ")"]
k :: Term a
k = Lam $ \x -> Lam $ \y -> x
s :: Term a
s = Lam $ \f -> Lam $ \g -> Lam $ \x -> f # x # (g # x)
omega :: Term a
omega = (Lam $ \f -> f # f) # (Lam $ \f -> f # f)
run t = t :: Term String
main = do
print $ run $ s -- \a -> \b -> \c -> a(c)(b(c))
print $ run $ s # k # k -- \a -> a
-- print $ run $ omega -- bad idea
Also, instead of writing this Lams, #s and stuff, you can parse string representations of lambda terms to HOAS — that's not harder than printing HOAS terms.
Printing the value of an expression is a common practice in debugging. For example, if I have a piece of code like this
my . super . cool . fUnCtIoN . chain $ value
and I am trying to see the output of fUnCtIoN . chain, I would add
my . super . cool . (\ x -> traceShow x x ) . fUnCtIoN . chain $ value
which is mouthful for a simple task like this, not to mention if I want to print many intermediate results:
(\ x -> traceShow x x )
. my
. (\ x -> traceShow x x )
. super
. (\ x -> traceShow x x )
. cool
. (\ x -> traceShow x x )
. fUnCtIoN
. (\ x -> traceShow x x )
. chain
$ value
It would just look awful. Is there a better way to do this?
Just use traceShowId! It does exactly what you're asking for.
my . super . cool . traceShowId . fUnCtIoN . chain $ value
Yes. join traceShow.
λ> import Control.Monad
λ> :t join
join :: Monad m => m (m a) -> m a
λ> :t join (+)
join (+) :: Num a => a -> a
In the case of the function monad, join f x = f x x, so join traceShow is equivalent to \x -> traceShow x x.
Or make a where clause that provides a new definition of (.):
--...your code without the nasty bits...
where
(.) f g a = f ( join traceShow (g a))
Which may just help, though there will be one more traceShow call than previously.
How about a helper function for adding a trace call to a function:
dbg :: Show a => String -> a -> a
dbg name x = trace (name ++ ": " ++ show x) x
main = do
let x = dbg "my" . my
. dbg "super" . super
. dbg "cool" . cool
. dbg "func" . fUnCtIoN
. dbg "chain" . chain
$ value
print x
my = (+1)
super = (+2)
cool = (+3)
fUnCtIoN = (+4)
chain = (+5)
value = 3
Output:
chain: 3
func: 8
cool: 12
super: 15
my: 17
18
You could write a higher-order function which takes a function of two arguments and uses the same value for both arguments.
applyBoth :: (a -> a -> b) -> a -> b
applyBoth f x = f x x
(Aside: this is join for the "reader" monad (->) a.)
Then you can use that combinator in curried form:
applyBoth traceShow
. my
. applyBoth traceShow
. super
. applyBoth traceShow
. cool
. applyBoth traceShow
. fUnCtIoN
. applyBoth traceShow
. chain
$ value
Or define an alias for applyBoth traceShow.
traceS = applyBoth traceShow
traceS
. my
. traceS
. super
. traceS
. cool
. traceS
. fUnCtIoN
. traceS
. chain
$ value
For maximum terseness points, you can automatically interleave traceS into a list of functions by folding it up:
showSteps :: Show a => [a -> a] -> a -> a
showSteps = foldr (\f g -> f . traceS . g) id
showSteps [my, super, cool, fUnCtIoN, chain] value
Edit Eh, what the hell... It's not entirely relevant, but here's how to make showSteps work when you want to pipeline your data through a number of types. It's an example of a program we wouldn't be able to write without GHC's advanced type system features (GADTs and RankNTypes in this instance).
Path is a GADT which explains how to walk through a directed graph of types, starting at the source type x and ending at the destination type y. It's parameterised by a category c :: * -> * -> *.
infixr 6 :->
data Path c x y where
End :: Path c z z
(:->) :: c x y -> Path c y z -> Path c x z
:-> reminds us that a journey of a thousand miles begins with a single step: if the category you're working in lets you go from x to y, and you can take a path from y to z, you can go from x to z.
End is for when you have reached your destination - it's pretty easy to walk from z to z by not walking at all.
So Path has the same recursive structure as a linked list, but with a more flexible approach to the things inside it. Rather than requiring all of its elements to have the same type, it gives you a way to join up arrows like dominos, as long as the return type of one arrow matches the input type of the next. (To use the mathematical jargon: if you view the underlying category c as a logical relation, then End augments c with reflexivity and :-> augments c with transitivity. Path c thus constructs the reflexive transitive closure of c. Another way of looking at this is that Path is the free category, much like [] is the free monoid; you can define instance Category (Path c) without any constraint on c.)
You can fold up a Path with exactly the same code as you use to fold up a list, but the type is more precise: the folding function can't know anything a priori about the types of the arrows inside the path.
foldr :: (forall x y. c x y -> r y z -> r x z) -> r z z -> Path c x z -> r x z
foldr f z End = z
foldr f z (x :-> xs) = f x $ foldr f z xs
At this point, I could define type-aligned sequences of functions (type TAS = Path (->)) and show you how f :-> g :-> h :-> End can be folded up into h . g . f, but since our goal is to print out all the intermediate values, we have to use a category with a tiny bit more structure than plain old ->. (Thanks to #dfeuer in the comments for the suggestion - I've adjusted the name he gave to better reflect the attention-seeking nature of my behaviour.)
data Showoff x y where
Showoff :: Show y => (x -> y) -> Showoff x y
Showoff is just like a regular function, except it assures you that the return value y will be Showable. We can use this extra bit of knowledge to write showSteps for paths in which each step is a Showoff.
type ShowTAS = Path Showoff
showSteps :: ShowTAS a b -> a -> b
showSteps path = foldr combine id path . traceS
where combine (Showoff f) g = g . traceS . f
It strikes me as a bit of a shame to use the impure traceS right in the midst of all this strongly typed fun. In real life I'd probably return a String along with the answer.
To prove that it does actually work, here is a chain of functions with varying types. We take in a String, read it into an Int, add one to it, convert it to a Float, then divide it by 2.
chain :: ShowTAS String Float
chain = Showoff read :-> plusOne :-> toFloat :-> divideTwo :-> End
where plusOne :: Showoff Int Int
plusOne = Showoff (+1)
toFloat :: Showoff Int Float
toFloat = Showoff fromIntegral
divideTwo :: Showoff Float Float
divideTwo = Showoff (/2)
ghci> showSteps chain "4"
"4"
4
5
5.0
2.5
2.5 -- this last one is not from a traceShow call, it's just ghci printing the result
Fun!
I have a homework where we are supposed to implement a functional datatype for any generic datatype. The specifications for the class are given in algebraic form. I'm having problems interpreting it and would like some help.
Here are the specifications that we got in algebraic form:
T is any generic datatype
Values V = {nil} ∪ T
Expressions E = {(cons e1 e2) ∣ e1, e2 ∈ E} ∪ V
Let L^n ⊂ E contain only expressions of the form (cons e1 (cons e2 . . . (cons en nil) . . .)
with all ei ∈ T for some n ∈ N. Let further L^∗ = ⋃n∈N L^n.
Functions
empty? ∶ E → {true,false}, e ↦ {true if e = nil, false otherwise
first ∶ L^∗ → E, (cons e1 e2) ↦ e1
rest ∶ L^∗ → E, (cons e1 e2) ↦ e2
prepend ∶ E × L^∗ → L^∗, e, (cons e1 e2) ↦ (cons e (cons e1 e2))
ith ∶ (L^∗)×N → E, (cons e1 . . . (cons ei. . . (cons en nil) . . .), i ↦ ei for 0 < i ≤ n
length ∶ L^∗ → N,l ∈ L^n ↦ n
Here are my interpretations:
Values: The values are of the class T and are nil(null)
Expressions:
Not sure if E is an array that consists of constants e1,e2... and that L^n is also an array that consists of e1,e2... en? Don't understand the (cons en nil) part... are the constants supposed to be nil?
Functions
Empty? function empty is supposed to check an element in the E array has a value or not.
first: returns first element
rest: returns a new array without the first element
prepend: puts the constant e first in the array.
ith: Not sure, maybe return n elements depending on input.
length: give length of the list L
The expressions are a bit messy but could anyone give me some feedback on this? Thanks in advance.
Let T be int. Then V is any int or a nul. Now, expressions can be cons(x y) where x,y are either ints or nuls or other expressions, for example:
1
cons(2 3)
cons(cons(4 5) cons(6 7))
L^n is a special kind of expression where the first argument of cons is limited to be T and the innermost atom must be nul. So this
cons(1 cons(2 cons(3 nul)))
is a valid L^n (specifically L^3), while this is not
cons(cons(4 5) cons(6 7))
(as a side note, L^n is what's called list in lisp, while E is a generic pair).
From your set of functions, only empty? (aka nil), first (=car) and rest (=cdr) are primitive, others can be defined in terms of them (provided you've also got the conditional), for example:
length(E) = 0 if empty(E) else 1 + length(rest(E))
I am attempting to generate a nice syntax for mapping a function over the values of an associative list, i.e. I want to write [x ↦ f y | (x ↦ y) ∈ l] for mapAList f l. I came up with
syntax
"_alist_map" :: "['b, pttrn, ('a × 'b) list] ⇒ ('a × 'b) list"
("[x ↦ _ | '(x ↦ _') ∈ _]")
which works, but causes term "(x,y)#[]" to tell me Inner syntax error at "(x , y ) # []" and the (x is shaded slightly different.
The reason seems that once x appears in a mixfix annotation, it now always a literal token to the grammer (a delimiter according to §7.4.1 of isar-ref) and no longer an identifier – just like the syntax for if ... then ... else ... prevents if from being a variable name
Can I somehow work around this problem?
Identifier names used in mixfix annotations cannot be used as identifiers any longer, and I don't know any way around that. Therefore, instead of using x as a variable name, you can pick a non-identifier symbol like \<xX> or \<mapAListvariable> and setup the LaTeX output to print this as x by adding \newcommand{\isasymmapAListvariable}{x} to your root.tex.
You can also add \<xX> or \<mapAListvariable> to the symbols file of Isabelle/JEdit (preferably in $ISABELLE_HOME_USER/etc/symbols) and assign it some Unicode point that will be used for display in Isabelle/JEdit.
I just made a small experiment with a function map_alist that hopefully corresponds to your mapAList and which is defined as follows:
fun map_alist :: "('b ⇒ 'c) ⇒ ('a × 'b) list ⇒ ('a × 'c) list"
where
"map_alist f [] = []" |
"map_alist f ((x, y) # xs) = (x, f y) # map_alist f xs"
Then existing syntax can be used which looks a little bit as you intended. Maybe this is an option?
lemma "map_alist f xs = [(x, f y). (x, y) ← xs]"
by (induct xs) auto
This is part of a homework assignment so my goal is to understand why this is wrong. As I mentioned before I'm using Moscow ML.
fun filter pred = let
fun f ([], a) = []
| f ([], a) = a
| f (e::L, a) = if pred e then (f L (e::a) ) else (f L a)
in
f
end
The error I get is:
| f (e::L, a) = if pred e then (f L (e::a) ) else (f L a)
^
Type clash: expression of type
'a list cannot have type
'a list * 'b list
I have been reading up on documentation, and it really hasn't helped. What I really don't get is where 'b list is coming from. In our assignment we have to use an accumulator with tail recursion. I believe where my error is is how filter calls the function f. Filter takes the predicate as an argument and f should take the list and accumulator which is initially the empty list.
I've tried calling f like: f L [], But in other examples we didn't actually have to call f with its argument and it was somehow passed automatically.
Anyway, any help understanding where my mistake is and guidance on how to fix the problem would be greatly appreciated.
-aitee
(also if anyone could give me any tips on decoding the type expression errors that could also be very beneficial.)
(f L (e::a)) would work only if f were a curried function, of type 'a list -> 'a list -> 'a list. You should be doing:
if pred e then (f (L, (e::a))) else (f (L,a))
Btw., SMLNJ complains of a redundant match (two f ([], a) clauses are given).
You're confusing tupled versus curried function calls. Your definition of f demands a tuple, (a,b), but you're passing it arguments as f a b. Try replacing your recursive calls to f L ... with f (L,...) instead.
The type error is a little unhelpful, but it's basically saying that you're passing a list when it expects a 2-tuple of lists.