Zip function in System F - lambda-calculus

Let's define list type
list = forall 'a, 'x. ('a -> 'x -> 'x) -> 'x -> 'x
where for instance
nil = Λ'a . Λ'x . λ(c : 'a -> 'x -> 'x) . λ(e : 'x) . e
cons = Λ'a . Λ'x . λ(head : 'a) . λ(tail : list 'a 'x) . λ(c : 'a -> 'x -> 'x) . λ(e : 'x) . c head (tail c e)
I am trying to define function zip of type
zip : forall 'a, 'b, 'c, 'x. ('a -> 'b -> 'c) -> list 'a 'x -> list 'b 'x -> list 'c 'x
That intuitively does
zip (+) [1,2,3] [4,5,6] = [5,7,9]
zip (λa . λb . a) [1,2] [2,3,4] = [1,2]
zip (λa . λb . a) [2,3,4] [1,2] = [2,3]
Note that it truncates the longer list to fit the shorter.
The main problem I encounter here is that I cannot "iterate" over two lists at once. How can implement such a function in system F? Is it even possible?

Okay, I managed to write solution for it. First of all let's define helper option type:
option = forall 'a, 'x. ('a -> 'x) -> 'x -> 'x
Which has two constructors:
none = Λ'a . Λ'x . λ (onsome : 'a -> 'x) . λ (onnone : 'x) . onnone
some = Λ'a . Λ'x . λ (elem : 'a) . λ (onsome : 'a -> 'x) . λ (onnone : 'x) . onsome elem
Next step are functions that will extract head and tail of the list. head will return option 'elemtype to handle empty list case, but tail will just return empty list on empty list (in similar manner to predecessor function on Church numerals)
head = Λ 'a . λ (l : list 'a) . l (λ (elem : 'a) . λ (p : option 'a) . some elem) none
tail = Λ 'a . λ (l : list 'a) .
pair_first
( l (λ (elem : 'a) . λ (p : pair (list 'a) (list 'a)) .
make_pair (list 'a) (list 'a)
(pair_second (list 'a) (list 'a) p)
(cons 'a elem (pair_second (list 'a) (list 'a) p)))
(make_pair (list 'a) (list 'a) (nil 'a) (nil 'a)))
The idea of head is that we start aggregating our list starting with none on empty list and for each new element from the left we set this element as our result wrapped by some to preserve typing.
tail on the other hand does not need option to be well defined, because in case of empty list we may just return an empty list. It looks very ugly, but uses same technique as predecessor of natural numbers. I assume pair interface is known.
Next, let's define listmatch function that will pattern match on given list
listmatch = Λ 'a . Λ 'x . λ (l : list 'a) . λ (oncons : 'a -> list 'a -> 'x) . λ (onnil : 'x) .
(head 'a l)
(λ (hd : 'a) . oncons hd (tail 'a l))
onnil
This function helps us distinguish empty list and non-empty list and perform some actions after its destruction.
Almost finally, we would like to have foldl2 function that given function f, empty case em and two lists [a,b,c] and [x,y] returns something like this: f(f(em a x) b y) (shrinks lists to the shorter one cutting off the tail).
It can be defined as
foldl2 =
Λ 'a . Λ 'b . Λ 'c .
λ (f : 'c -> 'a -> 'b -> 'c) . λ (em : 'c) . λ (la : list 'a) . λ (lb : list 'b) .
pair_first 'c (list 'b)
((reverse 'a la)
( λ (el : 'a) . λ (p : pair 'c (list 'b)) .
listmatch 'a (pair 'c (list 'b)) (pair_second 'c (list 'b) p)
(λ (hd : 'a) . λ (tl : list 'a) .
make_pair 'c (list 'b)
(f (pair_first 'c (list 'b) p) el hd)
tl)
(make_pair 'c (list 'b) (pair_first 'c (list 'b) p) (nil 'a))
)
(make_pair 'c (list 'b) em lb))
After this the zip is just in our hands:
zip =
Λ 'a . Λ 'b . Λ 'c .
λ (f : 'a -> 'b -> 'c) . λ (la : list 'a) . λ (lb : list 'b) .
reverse 'c
(foldl2 'a 'b 'c
(λ (lt : 'c) . λ (a : 'a) . λ (b : 'b) . cons 'c (f a b) lt)
(nil 'c) la lb)

Related

Can one use cons to do ((a . b) . (c . d)) and if not any other means or dot pair cannot have 2nd element like this?

;;; <- can one use cons to do ((a . b) . (c . d))?
(define x (cons a b)); nil -- should it be error
(define x (cons 'a 'b)); (a . b)
(define y (cons 'c 'd)); (c . d)
(define z00 (cons x y)) ; (((a . b) c . d) <- cannot use cons to do ((a . b) . (c . d))?
(define z01 (cons x 'y)) ; ((a . b) . y)
(define z10 (cons 'x y)) ; (x c . d)
(define z11 (cons 'x 'y)); (x . y))
(define z (list x y z00 z01 z10 z11))
; ((a . b) (c . d) ((a . b) c . d) ((a . b) . y) (x c . d) (x . y))
;;; and if not any other means or dot pair cannot have 2nd element like this?
Yes, you can. And the language has a wonderful predicate called equal? which will allow you to test this:
> (equal? (cons (cons 'a 'b) (cons 'c 'd))
'((a . b) . (c . d)))
#t
> (equal? '((a . b) . (c . d))
'((a . b) c . d))
#t
And you can even write a little display function which will confirm this:
(define (display-thing thing)
(if (cons? thing)
(begin
(display "(")
(display-thing (car thing))
(display " . ")
(display-thing (cdr thing))
(display ")"))
(display thing)))
And now
> (display-thing (cons (cons 'a 'b) (cons 'c 'd)))
((a . b) . (c . d))
> (display-thing '((a . b) . (c . d)))
((a . b) . (c . d))
> (display-thing '((a . b) c . d))
((a . b) . (c . d))
What this should all be telling you is that ((a . b) . (c . d)) and ((a . b) c . d) are merely different ways of writing a structurally identical object.
Pairs visualize differently based on their content. If the cdr of a pair contains the empty list it is a proper list an dthe dot and extra empty list is not shown:
(cons 'a '())
'(a . ())
; ==> (a)
A pair that has pair as it's cdr can be visualized as a list element without the . and extra parenthesis:
(cons 'b '(a))
'(b . (a))
; ==> (b a)
(cons 'b '(a . c))
'(b . (a . c))
; ==> (b a . c)
These are just made so that we can have (1 2 3) displayed instead of (1 . (2 . (3 . ()))) which is how it really is made.
If you were to not have a pair or a empty list in the cdr then it falls back to showing the dotted pair:
(cons 'a 'b)
'(a . b)
; ==> (a . b)
In your example '((a . b) . (c . d)) because there is a pair after a dot (eg. the cdr if the pair the visualization will remove the dot and one pair of parentheses and show it like ((a . b) c . d). This is the only acceptable correct way for a REPL to display this even though both your example and the display will be read in as the same structure.
There is a similar issue with numbers. In code you can use 10, #xa and #o12 to get the number 10 and the value will have no idea what format is was read in as and only show the base 10 in the REPL.
;;; ```
;;; <- can one use cons to do ((a . b) . (c . d))?
(define x (cons a b)); nil -- should it be error
(define x (cons 'a 'b)); (a . b)
(define y (cons 'c 'd)); (c . d)
(define z00 (cons x y)) ; (((a . b) c . d) <- cannot use cons to do ((a . b) . (c . d))?
(define z01 (cons x 'y)) ; ((a . b) . y)
(define z10 (cons 'x y)) ; (x c . d)
(define z11 (cons 'x 'y)); (x . y))
(define z22 (cons '(f g) '(h i)))
(define z2c (cons (cons 'f 'g) (cons 'h 'i)))
(define fgc (cons ('f 'g))); should it be error (nil)
; actually not as 'f is an exoression (quote f) and f for some reason is nil it becomes nil from quote of nil abd so is the second one. now (nil . nil) is (nil)
(define z (list x y z00 z01 z10 z11 z22 z2c fgc))
; ((a . b) (c . d) ((a . b) c . d) ((a . b) . y) (x c . d) (x . y))
;;; ```
;;; and if not any other means or dot pair cannot have 2nd element like this?
;;; possibly not
;;; as the cons join 2 pairs of dotted pairs and can generate one dotted pair
;;; ((a . b) . (c . d)) but the printing rule is reflected the list bias
;;; this new dotted pair will have the first element as (( a . b) ... print as
;;; ((a . b) ...
;;; the 2nd element it will consider whether it is an atom or another dotted pair
;;; (other possibilities like loop back or something else ... not sure)
;;; as (c . d) is a dotted pair the "printing" continues as a list would
;;;
;;; ((a . b) c ...
;;; however the second element of (c . d) is not a dotted pair but an atom and print as
;;; . d) will it becomes
;;; hence even though you form the binary tree head the dot pair would display as a partial list
;;; you can have a list of dotted pairs like z
;;; but not dotted pair of dotted pairs

cons* in scheme - how to implement

I tried to implement the cons* (https://scheme.com/tspl4/objects.html#./objects:s44).
Examples:
(cons* '()) -> ()
(cons* '(a b)) -> (a b)
(cons* 'a 'b 'c) -> (a b . c)
(cons* 'a 'b '(c d)) -> (a b c d)
this is what I did do far but I don't know how to replace the ?? to make the third example (the dot notion) work
(define cons*
(lambda x
(if
(null? x)
x
(if (list? (car (reverse x)))
(fold-right cons (car (reverse x)) (reverse (cdr (reverse x))))
???
)
)
)
)
Here's a lo-fi way using lambda -
(define cons*
(lambda l
(cond ((null? l) null)
((null? (cdr l)) (car l))
(else (cons (car l) (apply cons* (cdr l)))))))
Here's a way you can do it using match (Racket)
(define (cons* . l)
(match l
((list) null) ; empty list
((list a) a) ; singleton list
((list a b ...) (cons a (apply cons* b))))) ; two or elements
Often times patterns and order can be rearranged and still produce correct programs. It all depends on how you're thinking about the problem -
(define (cons* . l)
(match l
((list a) a) ; one element
((list a b) (cons a b)) ; two elements
((list a b ...) (cons a (apply cons* b))))) ; more
Or sugar it up with define/match -
(define/match (cons* . l)
[((list)) null]
[((list a)) a]
[((list a b ...)) (cons a (apply cons* b))])
All four variants produce the expected output -
(cons* '())
(cons* '(a b))
(cons* 'a 'b 'c)
(cons* 'a 'b '(c d))
'()
'(a b)
'(a b . c)
'(a b c d)
Personally, I'd use a macro instead of a function to transform a cons* into a series of cons calls:
(define-syntax cons*
(syntax-rules ()
((_ arg) arg)
((_ arg1 rest ...) (cons arg1 (cons* rest ...)))))
(define (writeln x)
(write x)
(newline))
(writeln (cons* '())) ;; -> '()
(writeln (cons* '(a b))) ;; -> '(a b)
(writeln (cons* 'a 'b 'c)) ;; -> (cons 'a (cons 'b 'c)) -> '(a b . c)
(writeln (cons* 'a 'b '(c d))) ;; -> (cons 'a (cons 'b '(c d))) -> '(a b c d)
A Simple Procedure
I think that you are making this more complicated than it needs to be. It seems best not to use lambda x here, since that would allow calls like (cons*) with no arguments. Instead, I would use (x . xs), and I would even just use the define syntax:
(define (cons* x . xs)
(if (null? xs)
x
(cons x (apply cons*
(car xs)
(cdr xs)))))
If there is only one argument to cons*, then xs is empty, i.e., (null? xs) is true, and that single argument x should be returned. Otherwise you should cons the first argument to the result of calling cons* again, with the first element of xs as the first argument, followed by the remaining arguments from xs. The trick here is that (cdr xs) returns a list, which will itself be put into a list thanks to the (x . xs) syntax. This is the reason for using apply, which will apply cons* to the arguments in the list.
This works for all of the test cases:
> (cons* '())
()
> (cons* '(a b))
(a b)
> (cons* 'a 'b 'c)
(a b . c)
> (cons* 'a 'b '(c d))
(a b c d)
Using Mutation
Taking a closer look at what a proper list really is suggests another approach to solving the problem. Consider a list like (a b c d). This is really a chain of cons cells that look like this:
(a . (b . (c . (d . ()))))
We would like to transform this list to an improper, or dotted, list:
(a . (b . (c . (d . ())))) --> (a . (b . (c . d)))
This transformed list is equivalent to (abc.d), which is what we would like the call to (cons* 'a 'b 'c 'd) to return.
We could mutate the proper list to an improper list by setting the cdr of the next-to-last pair to the car of the last pair; that is, by setting the cdr of (c . (d .()) to d. We can use the list-tail procedure to get at the next-to-last pair, list-ref to get at the car of the last pair, and set-cdr! to set the cdr of the next-to-last pair to the new value. After this, the list is no longer terminated by an empty list (unless the car of the final pair is itself an empty list!).
Here is a procedure proper->improper! that mutates a proper list to an improper list. Note that the input must be a proper list to avoid an error. If the input list contains only a single element, then that element is simply returned and no mutation takes place.
(define (proper->improper! xs)
(cond ((null? (cdr xs))
(car xs))
(else
(set-cdr! (list-tail xs (- (length xs) 2))
(list-ref xs (- (length xs) 1)))
xs)))
Now cons* can be defined simply in terms of proper->improper!:
(define (cons* . xs)
(proper->improper! xs))
Here, the arguments to cons* are packed up into a fresh list and passed to proper->improper! which effectively removes the terminal empty list from its input, returning a chain of pairs whose last cdr is the last argument to cons*; or if only one argument is provided, that argument is returned. This works just like the other solution:
> (cons* '())
()
> (cons* 'a)
a
> (cons* 'a 'b 'c 'd)
(a b c . d)
> (cons* 'a 'b '(c d))
(a b c d)
Real Life
In real life, at least in Chez Scheme, cons* is not implemented like any of these solutions, or even in Scheme at all. Instead Chez opted to make cons* a primitive procedure, implemented in C (I believe).

How to give a specific type of function as a parameter in Racket/Plait?

I am writing a function that takes in a function and a list as parameters. The parameter function and the list must have the same type of values. How do I ensure that?
I have tried:
(define ( (func -> 'a) [lst : (Typeof 'a)])
....)
However, I have not been able to get it working. I have also gone through the plait tutorial but didn't find anything relevant.
Is it even possible to have a function that accepts a function of a specific return type?
Is this what you are looking for?
(define f : (('a -> 'a) (listof 'a) -> string)
(lambda (func lst) "hello"))
Then:
(f (lambda ([x : number]) x) (list 1))
type checks, but:
(f (lambda ([x : number]) x) (list "foo"))
doesn't type check, because 'a is unified with string (from "foo"), but is also unified with number (from x), so a type mismatch occurs.
Note that
(define f : (('a -> 'a) (listof 'a) -> string)
(lambda (func lst) "hello"))
and
(define (f [func : ('a -> 'a)] [lst : (listof 'a)]) : string
"hello")
are different. In the former, 'a refers to the same type variable across arguments. In the latter, func's 'a and lst's 'a are different. Therefore, in the latter, the following expression type checks:
(f (lambda ([x : number]) x) (list "foo"))

Zip function in typed racket with rest arguments

I'm struggling with the syntax for a function that zips together any number of lists. I currently have:
(define (zip . [lsts : (Listof Any) *])
(apply map (inst list Any) lsts))
Which causes the following error when evaluated:
Error: struct:exn:fail:syntax /Applications/Racket
v6.6/collects/racket/private/kw.rkt:929:25: Type Checker: Bad
arguments to function in `apply':
Domains: (-> a b ... b c) (Listof a)
(Listof b) ... b
(-> a c) (Pairof a (Listof a))
Arguments: (-> Any * (Listof Any)) (Listof (Listof Any)) *
in: (#%app apply map (#%expression list) lsts)
Since these evaluate okay:
(apply map (inst list Any) '(("asd" 1 2) ("cat" 3 4)))
;;(("asd" "cat") (1 3) (2 4))
(define (test . [lsts : (Listof Any) *])
lsts)
(test '(1 2 3) '(2 3 "dogs"))
;;((1 2 3) (2 3 "dogs"))
I think the type checker's complaining about apply failing whenever no arguments are passed in, since I get a similar error trying to evaluate the following:
(apply map (inst list Any) '())
Error: struct:exn:fail:syntax /Applications/Racket
v6.6/collects/racket/private/kw.rkt:929:25: Type Checker: Bad
arguments to function in `apply':
Domains: (-> a b ... b c) (Listof a)
(Listof b) ... b
(-> a c) (Pairof a (Listof a))
Arguments: (-> Any * (Listof Any)) Null *
in: (#%app apply map (#%expression list) (quote ()))
But I'm not sure how to specify to the function that it'll take at least one argument (list).
The function map needs to take at least one list as an argument. Consider what would happen if you called zip with zero arguments. Then you would be calling map with zero lists, which isn't allowed. So, you have to restrict your zip function to take one or more arguments. You can do that by specifying an argument before the rest argument like this:
#lang typed/racket
(define (zip [lst : (Listof Any)] . [lsts : (Listof Any) *])
(apply map (inst list Any) lst lsts))
One more thing: This would be better if it were polymorphic.
#lang typed/racket
(: zip : (∀ (A) (-> (Listof A) (Listof A) * (Listof (Listof A)))))
(define (zip lst . lsts)
(apply map (inst list A) lst lsts))
Notice that the domain still needs to be (Listof A) (Listof A) * and not just (Listof A) *.
Update: Even more polymorphism
It's actually possible to make the polymorphism on this even better, so that if you give it exactly 3 lists, it produces a list of exactly-3-element lists. This version of zip would have the type
(: zip : (∀ (A B ...)
(-> (Listof A) ; first input
(Listof B) ... B ; next n inputs, where n is the number of B type-arguments
(Listof (List A B ... B)))))
However, if the body were (apply map list lst lsts), the list function would need the type
(∀ (A B ...) (-> A B ... B (List A B ... B)))
However, list used as a function value only has the type (All (a) (-> a * (Listof a))). Instead we can define a new function listd, which behaves exactly like list, but with the new type
;; (listd x y z) = (list x y z)
;; but it's assigned a type that's more convenient for `zip`
(: listd : (∀ (A B ...) (-> A B ... B (List A B ... B))))
(define (listd a . bs)
(cons a bs))
Using this, the dots-polymorphic version of zip can be defined like this:
(: zip : (∀ (A B ...)
(-> (Listof A) ; first input
(Listof B) ... B ; next n inputs, where n is the number of B type-arguments
(Listof (List A B ... B)))))
(define (zip lst . lsts)
(apply map (inst listd A B ... B) lst lsts))
Using it:
> (zip (list 'a 'b 'c) (list 1 2 3) (list "do" "re" "mi"))
- : (Listof (List (U 'a 'b 'c) Positive-Byte String))
'((a 1 "do") (b 2 "re") (c 3 "mi"))
I think the type checker's complaining about apply failing whenever no arguments are passed in
Correct. If you just want to assert that there will be at least one argument, without doing anything fancy with arity polymorphism:
(define example (cast '((1 2) (3 4) (5 6)) (Listof (Listof Number))))
(apply map (inst list Number) (assert example pair?))
The key piece of code here is (assert example pair?), which asserts that example is not an empty list. Typed Racket did not cause this problem - it is trying to protect you from the possibility of example being empty (with an unhelpful error message). assert bypasses Typed Racket's check without fixing this problem. However, (inst list Number) is required due to limitations in Typed Racket.

Transforming this into an expression

Hi guys i'm wanted to know if i have the correct expression for this picture, if not why please
(a((f(b c))(g h))e)
You're close, but not quite right. It'll be more clear if we build the list structure explicitly using cons; this is more like it:
(cons 'a
(cons (cons (cons 'f
(cons 'b 'c))
(cons 'g
(cons 'h '())))
(cons 'e '())))
=> '(a ((f b . c) g h) e)
Notice that in this part: (f b . c) we have an improper list, because the sublist doesn't end in null.
You answer is incorrect as it doesn't properly express the improper list (f b . c). Also the parentheses around g h are an error.
With dotted pairs the full expression would be:
'(a ((f b . c) g h) e)
Note that '(f b . c) is not the same as '(f (b c)).
See that '(f (b c)) is:
(cons 'f (cons (cons 'b (cons 'c '())) '()))
Rather than what '(f b . c) is:
(cons 'f (cons (cons 'b 'c) '()))
Note the improper list.

Resources