I'm taking a functional programming languages course and I'm having difficulty understanding recursion within the context of 'functions as arguments'
fun n_times(f , n , x) =
if n=0
then x
else f (n_times(f , n - 1 , x))
fun double x = x+x;
val x1 = n_times(double , 4 , 7);
the value of x1 = 112
This doubles 'x' 'n' times so 7 doubled 4 times = 112
I can understand simpler recursion patterns such as adding numbers in a list, or 'power of' functions but I fail to understand how this function 'n_times' evaluates by calling itself ? Can provide an explanation of how this function works ?
I've tagged with scala as I'm taking this course to improve my understanding of scala (along with functional programming) and I think this is a common pattern so may be able to provide advice ?
If n is 0, x is returned.
Otherwise, f (n_times(f , n - 1 , x)) is returned.
What does n_times do? It takes the result of calling f with x, n times, or equivalently: calls f with the result of n_times(f, n - 1, x) (calling f n-1 times on x).
Note by calling f i mean for example:
calling f 3 times: f(f(f(x)))
calling f 2 times: f(f(x))
Just expand by hand. I'm going to call n_times nx to save space.
The core operation is
nx(f, n, x) -> f( nx(f, n-1, x))
terminating with
nx(f, 0, x) -> x
So, of course,
nx(f, 1, x) -> f( nx(f, 0, x) ) -> f( x )
nx(f, 2, x) -> f( nx(f, 1, x) ) -> f( f( x ) )
...
nx(f, n, x) -> f( nx(f,n-1,x) ) -> f( f( ... f( x ) ... ) )
Function n_times has a base case when n = 0 and an inductive case otherwise. You recurse on the inductive case until terminating on the base case.
Here is an illustrative trace:
n_times(double, 4, 7)
~> double (n_times(double, 3, 7)) (* n = 4 > 0, inductive case *)
~> double (double (n_times(double, 2, 7))) (* n = 3 > 0, inductive case *)
~> double (double (double (n_times(double, 1, 7)))) (* n = 2 > 0, inductive case *)
~> double (double (double (double (n_times(double, 0, 7))))) (* n = 1 > 0, inductive case *)
~> double (double (double (double 7))) (* n = 0, base case *)
~> double (double (double 14))
~> double (double 28)
~> double 56
~> 112
It is the same recursion thinking what you know already, just mixed with another concept: higher order functions.
n_times gets a function (f) as a parameter, so n_times is a higher order function, which in turn is capable to apply this f function in his body. In effect that is his job, apply f n times to x.
So how you apply f n times to x? Well, if you applied n-1 times
n_times(f , n - 1 , x)
, then you apply once more.
f (n_times(f , n - 1 , x))
You have to stop the recursion, as usual, that is the n=0 case with x.
Related
I am trying to build my own calculator in Haskell with class declaration.
Firstly, I was trying to implement the addition, but here I got the error:
Couldn't match expected type 'Exp -> Double' with actual type 'Exp'
data Exp = Number Double | Add Exp Exp
class Calc a where
calculate :: a -> a -> Double
instance Calc Exp where
calculate (Add a b) = a + b
What do I have to change?
You don't really need, nor would I recommend, a class here. Just define an ordinary function that handles each of the data constructors separately. (Use a class only if you expect to have multiple types that can be used with calculate.)
In either case, calculate takes a single argument, as your type represents an entire expression, operands and operator together.
calculate :: Exp -> Double
calculate (Number n) = ...
calculate (Add a b) = ...
Keep in mind that a and b are values of type Exp, not numbers (i.e., types with a Num instance) that you can add directly. (This suggests you need to recursively calculate the value of a and b before you can actually perform addition.)
Some test cases for you to consider:
n1 = Number 2.0
n2 = Add (Number 3.0) (4.0))
n3 = Add n1 n2
map calculate [n1, n2, n3] == [2.0, 7.0, 9.0]
As suggested by chepner, your calculate function need only take a single argument. You also need to account for the possibility that each Exp value maybe be a Number and handle that in the function.
Since a and b are Exp values and not Num instances, you will need to apple calculate to them.
data Exp = Number Double | Add Exp Exp
class Calc a where
calculate :: a -> Double
instance Calc Exp where
calculate (Number n) = n
calculate (Add a b) = a' + b'
where a' = ...
where b' = ...
But you might consider making Exp an instance of Num. The below is incomplete as it doesn't account for actual expressions except for +, but it might hint at what's possible.
instance Num Exp where
(+) (Number n1) (Number n2) = Number (n1 + n2)
(+) (Add a b) c#(Number _) = a + b + c
(+) a#(Number _) (Add b c) = a + b + c
(+) (Add a b) (Add c d) = a + b + c + d
(*) (Number n1) (Number n2) = Number (n1 * n2)
abs (Number n) = Number (abs n)
negate (Number n) = Number (negate n)
signum (Number n) = Number (signum n)
fromInteger n = Number (fromIntegral n)
ghci> case Add (Number 5) (Number 6) + Number 7 of { Number n -> n }
18.0
def calculate(f: Int => Int, sumProd:(Int, Int)=>Int, n: Int, a:Int, b:Int):Int =
if (a>b) n
else sumProd(f(a), calculate(f, sumProd, n, a+1, b))
This scala function can in a chosen number area (a to b) do chosen calculations with them:
example calling:
calculate(x=>2*x, (x,y)=>x+y, 0, 2 , 4)
this calculates: 2*2 + 2*3 + 2*4 = 18
Which folding(right- or left folding) is this function using? And how to see that?
further example callings for the function:
calculate(x=>2+x, (x,y)=>x*y,1, 2 , 4)
calculate(x=>2+x, (a,b)=>a+b,0, 1, 5)
calculate(x=>2*x, (a,b)=>a+b,0, 1, 5)
What you have is equivalent to, in pseudocode,
calculate(f, sumProd, n, a, b)
=
fold-right( sumProd, n, map(f, [a .. b]))
where [a .. b] denotes a list of numbers from a to b, inclusive, increasing by the step of 1. In other words, it is the same as
=
sumProd( f(a),
sumProd( f(a2),
sumProd( f(a3),
...
sumProd( f(b), n) ... )))
I would like to add binary numbers to my untyped lambda calculus library, but I'm stuck with the succ and pred functions. I am using a representation outlined in a paper by T. Mogensen and while most of the functions defined there work, succ and pred are returning erroneous results.
I'm pretty sure I got the representation right:
dec bin De Bruijn classic
0 0 λλλ3 λa.λb.λc.a
1 1 λλλ13 λa.λb.λc.c a
2 10 λλλ2(13) λa.λb.λc.b (c a)
3 11 λλλ1(13) λa.λb.λc.c (c a)
4 100 λλλ2(2(13)) λa.λb.λc.b (b (c a))
5 101 λλλ1(2(13)) λa.λb.λc.c (b (c a))
6 110 λλλ2(1(13)) λa.λb.λc.b (c (c a))
7 111 λλλ1(1(13)) λa.λb.λc.c (c (c a))
8 1000 λλλ2(2(2(13))) λa.λb.λc.b (b (b (c a)))
The tuples and projections also look fine:
tuple De Bruijn classic
[T, F] λ1(λλ2)(λλ1) λa.a (λb.λc.b) (λb.λc.c)
[T, F, F] λ1(λλ2)(λλ1)(λλ1) λa.a (λb.λc.b) (λb.λc.c) (λb.λc.c)
[T, F, F, T] λ1(λλ2)(λλ1)(λλ1)(λλ2) λa.a (λb.λc.b) (λb.λc.c) (λb.λc.c) (λb.λc.b)
πkn De Bruijn classic
π12 λ1(λλ2) λa.a (λb.λc.b)
π22 λ1(λλ1) λa.a (λb.λc.c)
Shifting up with a 0-bit (shl0) and 1-bit (shl1) works well in tests:
SHL0 ≡ λnbzo.z (n b z o) = λ λ λ λ 2 (4 3 2 1)
SHL1 ≡ λnbzo.o (n b z o) = λ λ λ λ 1 (4 3 2 1)
But succ and pred that depend on the terms listed above don't:
SUCC ≡ λn.π22 (n Z A B) ≡ λ π22 (1 Z A B) where
Z ≡ [ZERO, ONE] // encoded like in the first piece of code
A ≡ λp.p (λnm.[SHL0 n, SHL1 n]) ≡ λ 1 (λ λ [SHL0 2, SHL1 2])
B ≡ λp.p (λnm.[SHL1 n, SHL0 m]) ≡ λ 1 (λ λ [SHL1 2, SHL0 1])
PRED ≡ λn.π22 (n Z A B) ≡ λ π22 (1 Z A B) where
Z ≡ [ZERO, ZERO] // encoded like in the first piece of code
A ≡ λp.p (λnm.[SHL0 n, SHL1 m]) ≡ λ 1 (λ λ [SHL0 2, SHL1 1])
B ≡ λp.p (λnm.[SHL1 n, SHL0 n]) ≡ λ 1 (λ λ [SHL1 2, SHL0 2])
Example results:
succ 0 = λa.λb.λc.c a / λλλ13 ok
succ 1 = λa.λb.λc.b (b c) / λλλ2(21) wrong, expected λλλ2(13)
succ 2 = λa.λb.λc.c (b (c (λd.λe.λf.e (b d e f)) (λd.λe.λf.f (b d e f)))) / λλλ1(2(1(λλλ2(5321))(λλλ1(5321)))) wrong, expected λλλ1(13)
succ 3 = λa.λb.λc.b (b c) / λλλ2(21) wrong, expected λλλ2(2(13))
pred 1 = λa.λb.λc.b a / λλλ23 wrong-ish, expected λλλ3; it's just a leading zero, but it's stated that those should only be caused by inputs that are powers of 2
pred 2 = λa.λb.λc.c (b c) / λλλ1(21) wrong, expected λλλ13
pred 3 = λa.λb.λc.b (b a) / λλλ2(23) wrong, expected λλλ2(13)
pred 4 = λa.λb.λc.c (b c) / λλλ1(21) wrong, expected λλλ1(13)
My term evaluator was tested against hundreds of terms, so I'm pretty confident in it; I'm suspecting that either I'm misreading something or something is misprinted. Am I missing something?
So, as ljedrz mentionned, we manage to get Morgensen numerals working in a separate chat. In this answer, I'll just describe shortly how it works in general.
The question was: « I'm suspecting that either I'm misreading something or something is misprinted. Am I missing something? »
tl;dr: it turns out some tricky stuff related to evaluation order caused the problem. The Morgensen numerals as presented in the question do work.
Longer answer: how does succ work ?
N.B.: in the following b_n is always assumed to be 1, as in the original paper.
The idea behind Morgensen numerals is to have a number n = b_n ... b_2 b_1 encoded as \z.\x_0.\x_1. x_{b_1} ( x_{b_2} (... ( x_{b_n} z ) ...) ). This is pretty unreadable, but it becomes clearer if it is stated that way:
A number n is a term which expects 3 arguments, and when applied, returns x_{b_1} ( x_{b_2} (... ( x_{b_n} z ) ...) )
Well, that's still unclear. If we look deeper, we see that a number n apply recursively either x_0 or x_1, starting from a term z. Notice that the recursive call is made "from left to right", i.e., if I have a number b_n b_{n-1} ... b_2 b_1, then the recursive calls are evaluated in this order:
first b_n z, let it be i_{n-1}
then b_{n-1} i_{n-1}, let it be i_{n-2}
...
and finally i_1 b_1
(Well, the evaluation strategy determines the exact evaluation order, to I think it is easy to think it evaluates like that)
Relation with fold function on lists
Actually, when I realised that, it just made me think about the fold_left function of a bit list: say you have a list of bits l = [b_n; ... ; b_2; b_1], then you can do the following:
fold_left (fun prev_acc -> fun b -> if b = 0 then x_0 prev_acc else x_1 prev_acc) z l
let f be
fun prev_acc -> fun b -> if b = 0 then x_0 prev_acc else x_1 prev_acc
which returns (according to Ocaml doc)
f (f (... (f z b_n) ...) b_2) b_1
which evaluates to:
f z b_n evaluates to x_{b_n} z, that is i_{n-1} as above.
...
f i_{1} b_1, as above.
Conclusion, you can absolutely think of Morgensen numerals as a fold_left on a list (or fold_right, depending on how you imagine the list).
Getting the succ of a number
Getting the succ of a number is getting n+1. The binary increment as a nice property:
if m = bn ... bi bj bk ... b1 with bj being the first 0 (i.e. bk = ... = b1 = 1), then m + 1 = bn ... bi 1 0 ... 0
This can be illustrated:
bn ... bi 0 1 1 1 1 1
if I add 1, then I got (by detailling all steps):
bn ... bi 0 1 1 1 1 1
+1
--------------------------
bn ... bi 0 1 1 1 1 0
+1 < I have a carry here, which gets propagated
...
--------------------------
bn ... bi 0 0 0 0 0 0
+1 < The carry ends up here
--------------------------
bn ... bi 1 0 0 0 0 0 < This is our result of doing a +1.
A good remark is to notice that (bn ... bi 0 1 ... 1) + 1 is (bn ... bi 0) + 1 appended to 0 ... 0, and more generally, it also applies for any bj: (bn ... bi bj 1 ... 1) + 1 is (bn ... bi bj) + 1 appended to 0 ... 0.
This seems pretty good, only one problem, the carry is propagated from right to left (LSB to MSB), while Morgensen numerals are from MSB to LSB.
To solve this last problem, we can be speculative: suppose I have a number b_n ... bi bj bk ... b1, and I want to have its successor. I will have to compute it reccursively, but only from MSB to LSB.
That is, if I am at "step bj", I can only work with the subsequence bn ... bi and bj itself.
This allows us, for instance, to compute the succ of bn ... bi. Now comes the speculative part:
I know that, if after bj, there are only 1, then after the previous remark, then the successor is ((bn ... bi bj) + 1)::(0 ... 0)
However, if there is a 0 in bk ... b1, then the bits (bn ... bi bj) remains unchanged.
So the idea is to return both possibilities at each bit, in a tuple. Informally, the function passed to fold_left looks like that:
fun tuple_msb -> fun bj ->
(original_msb, incr_msb)
where (1) tuple_msb is a tuple containing (bn ... bi, (bn ... bi) + 1); and where (2) original_msb and incr_msb are computed depending on bj. Indeed:
if bj is 0, then (bn ... bi bj) + 1 = (bn ... bi 0) + 1 = (bn ... bi 1)
if bj is 1, then (bn ... bi bj) + 1 = (bn ... bi 1) + 1 = ((bn ... bi) + 1)::0.
That is, the complete function to pass to fold_left is the following:
(* We keep the original sequence on the left of the tuple, the incremented `bn ... bi bj` on the right *)
fun tuple_msb -> fun bj ->
if bj = 0 then
(tuple_msb._1 :: 0, tuple_msb._1 :: 1)
else
(tuple_msb._1 :: 1, tuple_msb._2 :: 0)
And the base case (i.e. the starting element is the tuple (0, 1))
And from here, it is easy to go back to Morgensen unreadable terms (there is a small hidden shortcut here about the order of the arguments, but it really doesn't matter):
We can identify fun tuple_msb -> (tuple_msb._1 :: 0, tuple._1 :: 1) as being x_0 and fun tuple_msb -> (tuple_msb._1 :: 1, tuple_msb._2 :: 0) as being x_1 according to the notation we had at the begining for x_0 and x_1, and the base case (i.e. z at the begining is (0, 1)).
To get the final successor, we have to get the right part of the returned tuple, hence the final
let succ n =
let ret_tuple = n z x_0 x_1 in
ret_tuple._2
or in lambda terms:
succ' = λn. π22 (n z x_0 x_1)
with all π22, z, x_0 and x_1 accordingly defined.
Our succ' is a bit different from the proposed succ, i.e. x_0 is not exactly A and x_1 is not exactly B, but this last step is easy and left for the interested reader ;-)
When Bromind informed me that their slightly different definition of succ works in Ocaml+utop I ported it and tested with my library. It failed as well, though, so I started analyzing all the terms involved only to see that everything looked pretty much the same as in my implementation. Convinced that both my evaluator is solid and those definitions are valid, I kept looking until I found the actual cause.
Not to spoil the fun too soon, though, I'll add that I actually contacted prof. Mogensen about this and he was kind enough to provide me with high-level evaluation steps for the expression succ 1:
succ 1
= (λn.π² (n Z A B)) |1|
= (λn.π² (n Z A B)) (λz01.1 z)
= π² ((λz01.1 z) Z A B)
= π² (B Z)
= π² ((λp.p (λnm.[↑1 n, ↑0 m])) [|0|, |1|])
= π² ([|0|, |1|] (λnm.[↑1 n, ↑0 m]))
= π² ((λx.x |0| |1|) (λnm.[↑1 n, ↑0 m]))
= π² ((λnm.[↑1 n, ↑0 m]) |0| |1|)
= π² [↑1 |0|, ↑0 |1|]
= ↑0 |1|
= (λn.λz01.0 (n z 0 1)) |1|
= λz01.0 (|1| z 0 1)
= λz01.0 ((λz01.1 z) z 0 1)
= λz01.0 (1 z)
= |2|
This solution is 100% valid, but it is too high-level to compare with my implementation, so I have decided to detect the exact point where the definition fails with "low-level" β-reductions.
Below is the initial expression (succ 1) in De Bruijn index notation:
(λ(λ1(λλ1))(1(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))))(λλλ13)
and its reduction steps (normal order; others won't work either):
(λ(λ1(λλ1))(1(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))))(λλλ13)
^ ^ ^^^^^^^
↳ becomes λλλ13
(λ1(λλ1))((λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))))
^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
↳ becomes (λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))
(λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
^ ^ ^^^^^^^^^^^^^^^^^
↳ becomes λ1(λλλ3)(λλλ13)
(λλ1(λ1(λλλ3)(λλλ13)))(λ1(λλλ1((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - is dropped
(λ1(λ1(λλλ3)(λλλ13)))(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
↳ becomes λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))
(λ1(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)))(λ1(λλλ3)(λλλ13))(λλ1)
^^ ^^^^^^^^^^^^^^^^^
↳ becomes λ1(λλλ3)(λλλ13)
(λ1(λλλ3)(λλλ13))(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))(λλ1)
^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
↳ becomes λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1)
(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ3)(λλλ13)(λλ1)
^ ^^^^^^ - is dropped
(λλ1((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ13)(λλ1)
^ ^ ^^^^^^^
↳ becomes λλλ13
(λ1((λλλλ1(4321))(λλλ13))((λλλλ2(4321))1))(λλ1)
^^ ^
↳ becomes (λλ1) ↳ becomes (λλ1)
(λλ1)((λλλλ1(4321))(λλλ13))((λλλλ2(4321))(λλ1))
^ ^^^^^^^^^^^^^^^^^^^^^^ - is dropped
(λ1)((λλλλ2(4321))(λλ1))
^^ ^^^^^^^^^^^^^^^^^^^^
↳ becomes (λλλλ2(4321))(λλ1)
(λλλλ2(4321))(λλ1)
^ ^ ^^^^^
↳ becomes λλ1
λλλ2((λλ1)321)
^ ^ - is dropped
λλλ2((λ1)21)
^^ ^
↳ becomes 2
λλλ2(21) // fail
It's a subtle one: both A and B need to have their tuple in an unnormalized/destructured form in order for the succ and pred definition to work with pure β-reduction evaluators. I noticed this because my implementation used normalized tuples while Bromind's didn't.
Let's see the correct definition:
(λ(λ1(λλ1))(1(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))))(λλλ13)
and its reduction steps (normal order again):
(λ(λ1(λλ1))(1(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))))(λλλ13)
^ ^ ^^^^^^^
↳ becomes λλλ13
(λ1(λλ1))((λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))))
^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
↳ becomes (λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))
(λλλ13)(λ1(λλλ3)(λλλ13))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
^ ^ ^^^^^^^^^^^^^^^^^
↳ becomes λ1(λλλ3)(λλλ13)
(λλ1(λ1(λλλ3)(λλλ13)))(λ1(λλ(λλλ132)((λλλλ2(4321))2)((λλλλ1(4321))2)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - is dropped
(λ1(λ1(λλλ3)(λλλ13)))(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))(λλ1)
^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
↳ becomes λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))
(λ1(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)))(λ1(λλλ3)(λλλ13))(λλ1)
^^ ^^^^^^^^^^^^^^^^^
↳ becomes λ1(λλλ3)(λλλ13)
(λ1(λλλ3)(λλλ13))(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))(λλ1)
^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
↳ becomes λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1)
(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ3)(λλλ13)(λλ1)
^ ^ ^^^^^^
↳ becomes λλλ3
(λ(λλλ132)((λλλλ1(4321))(λλλ3))((λλλλ2(4321))1))(λλλ13)(λλ1)
^ ^ ^^^^^^^
↳ becomes λλλ13
(λλλ132)((λλλλ1(4321))(λλλ3))((λλλλ2(4321))(λλλ13))(λλ1)
^ ^ ^^^^^^^^^^^^^^^^^^^^^
↳ becomes (λλλλ1(4321))(λλλ3)
(λλ1((λλλλ1(4321))(λλλ3))2)((λλλλ2(4321))(λλλ13))(λλ1)
^ ^ ^^^^^^^^^^^^^^^^^^^^^^
↳ becomes (λλλλ2(4321))(λλλ13)
(λ1((λλλλ1(4321))(λλλ3))((λλλλ2(4321))(λλλ13)))(λλ1)
^^ ^^^^^
↳ becomes λλ1
(λλ1)((λλλλ1(4321))(λλλ3))((λλλλ2(4321))(λλλ13))
^ ^^^^^^^^^^^^^^^^^^^^^ - is dropped
(λ1)((λλλλ2(4321))(λλλ13))
^^ ^^^^^^^^^^^^^^^^^^^^^^
↳ becomes (λλλλ2(4321))(λλλ13)
(λλλλ2(4321))(λλλ13)
^ ^ ^^^^^^^
↳ becomes λλλ13
λλλ2((λλλ13)321)
^ ^ ^
↳ becomes the right-hand side 3 and gets an index upgrade due to extra abstractions
λλλ2((λλ15)21)
^ ^
↳ gets its index downgraded
λλλ2((λ14)1)
^^ ^
↳ becomes the right-hand side 1; 4 gets an index downgrade
λλλ2(13) // aww yiss
The crucial difference can be seen below:
(λλλ1((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ3)(λλλ13)(λλ1) // normalized tuple
^ ^^^^^^ - is dropped
(λλ(λλλ132)((λλλλ1(4321))2)((λλλλ2(4321))1))(λλλ3)(λλλ13)(λλ1) // unnormalized tuple
^ ^ ^^^^^^
↳ becomes λλλ3
If A or B contains a normalized tuple, the zero from the Z tuple is dropped and out of the game, so the crucial step π² ((λnm.[↑1 n, ↑0 m]) |0| |1|) => π² [↑1 |0|, ↑0 |1|] can't fully happen.
Below is the difference between the erroneous (for pure β-reduction evaluators) and correct full (including Z, A, B) definition of succ:
λ PI22 (1 [ZERO, ONE] (λ 1 (λ λ [SHL0 2, SHL1 2])) (λ 1 (λ λ [SHL1 2, SHL0 1]))) // wrong
λ PI22 (1 [ZERO, ONE] (λ 1 (λ λ TUPLE2 (SHL0 2) (SHL1 2))) (λ 1 (λ λ TUPLE2 (SHL1 2) (SHL0 1)))) // right
where TUPLE2 ≡ λ λ λ 1 3 2
Here is a function that takes a pair of Integral
values and divides them:
divide_v1 :: Integral a => (a, a) -> a
divide_v1 (m, n) = (m + n) `div` 2
I invoke the function with a pair of Integral
values and it works as expected:
divide_v1 (1, 3)
Great. That's perfect if my numbers are always Integrals.
Here is a function that takes a pair of Fractional
values and divides them:
divide_v2 :: Fractional a => (a, a) -> a
divide_v2 (m, n) = (m + n) / 2
I invoke the function with a pair of Fractional
values and it works as expected:
divide_v2 (1.0, 3.0)
Great. That's perfect if my numbers are always Fractionals.
I would like a function that works regardless of whether the
numbers are Integrals or Fractionals:
divide_v3 :: Num a => (a, a) -> a
divide_v3 (m, n) = (m + n) ___ 2
What operator do I use for _?
To expand on what AndrewC said, div doesn't have the same properties that / does. For example, in maths, if a divided by b = c, then c times b == a. When working with types like Double and Float, the operations / and * satisfy this property (to the extent that the accuracy of the type allows). But when using div with Ints, the property doesn't hold true. 5 div 3 = 1, but 1*3 /= 5! So if you want to use the same "divide operation" for a variety of numeric types, you need to think about how you want it to behave. Also, you almost certainly wouldn't want to use the same operator /, because that would be misleading.
If you want your "divide operation" to return the same type as its operands, here's one way to accomplish that:
class Divideable a where
mydiv :: a -> a -> a
instance Divideable Int where
mydiv = div
instance Divideable Double where
mydiv = (/)
In GHCi, it looks like this:
λ> 5 `mydiv` 3 :: Int
1
λ> 5 `mydiv` 3 :: Double
1.6666666666666667
λ> 5.0 `mydiv` 3.0 :: Double
1.6666666666666667
On the other hand, if you want to do "true" division, you would need to convert the integral types like this:
class Divideable2 a where
mydiv2 :: a -> a -> Double
instance Divideable2 Int where
mydiv2 a b = fromIntegral a / fromIntegral b
instance Divideable2 Double where
mydiv2 = (/)
In GHCi, this gives:
λ> 5 `mydiv2` 3
1.6666666666666667
λ> 5.0 `mydiv2` 3.0
1.6666666666666667
I think you are looking for Associated Types which allows for implicit type coercion and are explained quite nicely here. Below is an example for the addition of doubles and integers.
class Add a b where
type SumTy a b
add :: a -> b -> SumTy a b
instance Add Integer Double where
type SumTy Integer Double = Double
add x y = fromIntegral x + y
instance Add Double Integer where
type SumTy Double Integer = Double
add x y = x + fromIntegral y
instance (Num a) => Add a a where
type SumTy a a = a
add x y = x + y
I need to write some function NTimesComposition(f:(int * int -> int), n:int) which receives some function f and integer n and after doing composition of f, n times, like this f(x,(f(x,f(x,y)))) <- (here for example n = 3) I began to write it on smlnj, but it seems more complicated than I thought thanks in advance for any idea:
NTimesComposition(f:(int * int -> int), n:int)
if n = 1 then fn(x,y) => f(x, y ) else NTimesComposition...//here I'm stuck, must be recurstion
You already got it for n = 1 and you most likely just forgot to pass the (x, y) in the recursive call for n > 1. Obviously here it needs to be something of the form fn (x,y) => f (x, ...) where the ... part is where your recursive calls is going to be.
If you had forgot the (x,y) in the recursive part making it fn (x,y) => NTimesComposition (f, n-1) then you would end up building a chain of anonymous functions as "long" as your argument n describes. That would result in a different type of your NTimesComposition function depending on what n you supply which is not valid due to the way SML's type system works (Hindley-Milner).
The following two functions will do the job for you
fun foo (f, 1) = (fn xy => f xy)
| foo (f, n) = (fn (x,y) => f(x, foo (f, n-1) (x,y)))
and
fun baz (f, 1) xy = f xy
| baz (f, n) (x,y) = f(x, foo (f, n-1) (x,y))
where the first resembles your code the most using the anonymous function.