Applying to potentially empty lists in Typed Racket - racket

The following is no problem in untyped racket:
(apply hc-append
(for/list ([colour '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) colour)))
The result is a pict with three differetnly coloured squares.
But it's not possible in Typed Racket. First, for/list needs a type annotation:
(apply hc-append
(for/list : (Listof pict)
([colour '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) colour)))
But then, the types of (curry apply hc-append) and the result of the (for/list …) form don't align:
; […] Type Checker: Bad arguments to function in `apply':
; Domains: pict pict *
; Real pict pict *
; Arguments: (Listof pict)
This error message looks puzzling, but it comes down to the fact that (Listof pict) may be empty, and hc-append's type signature requires pict pict *, i.e. at least one pict.
But this works in both typed and untyped Racket:
(apply hc-append (map (curry colorize (filled-rectangle 10 10))
'("red" "green" "blue")))
The type of the (map …) form is (Pairof pict (Listof pict)). Great! That's a non-empty list. But I can't use that in the for/list form as a type annotation, the type checker rejects it.
Even if I carefully pick apart the types:
(let ([potentially-empty-list
(for/list : (Listof pict)
([c '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) c))])
(cond
[(null? potentially-empty-list) (blank)]
[else (apply hc-append #{(list (first potentially-empty-list)
(rest potentially-empty-list))
: (Pairof pict (Listof pict))})]))
I end up with a very confusing message, essentially the same, but now it defies my original justification! Namely, that the list may be empty. The type clearly states that it may not be empty! Racket rejects it:
; […] Type Checker: Bad arguments to function in `apply':
; Domains: pict pict *
; Real pict pict *
; Arguments: (List pict (Listof pict))
Two things confuse me:
Instead of my original (Pairof pict (Listof pict)), Racket turns the type into (List pict (Listof pict)). I think that's OK, because the two should be equivalent.
But if they're equivalent then I would expect the (apply …) form to work, as in the (map …) case! There, too, we use (apply hc-append ls) where ls has type (Pairof pict (Listof pict)).
My intuiton is that at least the last example should work, but it does not, and I don't understand why.

Your following code is pretty close.
(let ([potentially-empty-list
(for/list : (Listof pict)
([c '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) c))])
(cond
[(null? potentially-empty-list) (blank)]
[else (apply hc-append #{(list (first potentially-empty-list)
(rest potentially-empty-list))
: (Pairof pict (Listof pict))})]))
However, there are two mistakes.
(list (first ....) (rest ...)) is a list of two elements, where the first element is a pict and the second element is a (Listof pict). I.e. it has type (List pict (Listof pict)). I think you instead want to use (cons (first ....) (rest ...)), which has type (Pairof pict (Listof pict) = (Listof pict) that guarantees it has at least one element.
You should use #{(cons ...) :: (Pairof ...)} instead. The #{x : t} notation is only valid in binding positions, particularly when x is a variable. The #{e :: t} notation however allows e to be an arbitrary expression, which is what you are doing here. Typed Racket does appear to be buggy in a sense that it doesn't report a misuse of #{x : t} when x is not a variable. I'll file a bug report.
Following program works:
#lang typed/racket
(require typed/pict)
(let ([potentially-empty-list
(for/list : (Listof pict)
([c '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) c))])
(cond
[(null? potentially-empty-list) (blank)]
[else (apply hc-append #{(cons (first potentially-empty-list)
(rest potentially-empty-list))
:: (Pairof pict (Listof pict))})]))

Here's a simplification I came up with thanks to Sorawee's answer:
(: safe-apply (∀ (E R) (→ R (→ E E * R) (Listof E) R)))
(define (safe-apply def f ls)
(cond
[(null? ls) def]
[else (apply f ls)]))
The fun part is that Occurrence Typing in Racket will make ls have type (Pairof E (Listof E)) in the else clause of cond.
This can then be used like so:
(safe-apply (blank)
hc-append
(for/list : (Listof pict)
([colour '("red" "green" "blue")])
(colorize (filled-rectangle 10 10) colour)))

Related

Polymorphic function `car' could not be applied to arguments

I want to use typed/racket to implement the "LET" language in eopl, but I encounter problems:
I try to implement three functions( empty-env, extend-env, apply-env ), but I don't know how to label the type information, and racket cannot automatically deduce the types of these three functions.
I tried to use any, but I still encountered problems:
(: empty-env (-> Any))
(define empty-env
(lambda () (list 'empty-env)))
(: extend-env (-> Any Any Any Any))
(define extend-env
(lambda (var val env)
(list 'extend-env var val env)))
(: apply-env (-> Any Any Any))
(define apply-env
(lambda (env search-var)
(cond [(eqv? (car env) 'empty-env) (None)]
[(eqv? (car env) 'extend-env)
(let ([saved-var (cadr env)]
[saved-val (caddr env)]
[saved-env (cadddr env)])
(if (eqv? search-var saved-var)
saved-val
(apply-env saved-env search-var)))]
[else (None)])))
The error prompted by racket is:
Type Checker: Polymorphic function `car' could not be applied to arguments:
Domains: (Listof a)
(Pairof a b)
Arguments: Any
in: (car env)
How can I correctly label the type information
#lang typed/racket
(define-type EmptyEnv '(empty-env))
(define-type Env (U EmptyEnv
(List 'extend-env Any Any Env)))
(define (None) 'None)
(: empty-env (-> Env))
(define empty-env
(lambda () (list 'empty-env)))
(: extend-env (-> Any Any Env Env))
(define extend-env
(lambda (var val env)
(list 'extend-env var val env)))
(: apply-env (-> Env Any Any))
(define apply-env
(lambda (env search-var)
(cond [(eqv? (car env) 'empty-env) (None)]
[(eqv? (car env) 'extend-env)
(let ([saved-var (cadr env)]
[saved-val (caddr env)]
[saved-env (cadddr env)])
(if (eqv? search-var saved-var)
saved-val
(apply-env saved-env search-var)))]
[else (None)])))
(define env (extend-env 'a 42 (empty-env)))
env
(apply-env env 'a)
I've found that having a list of arbitrary length of mixed types doesn't really play well with typed Racket (True, but not relevant to OP's original code; I was looking at an earlier version of mine when I said that), and that Any should be used sparingly (If you're using it for all types, might as well use normal Racket, for one). Using a list of pairs for your environment (Or better yet a list of structs, but I'm not familiar with EOPL and what it covers) is the way to go here. Something like:
#lang typed/racket
;; Type that associates a symbol with an arbitrary value
(define-type Binding (Pair Symbol Any))
;; A list of these is your environment/symbol table.
(define-type Env (Listof Binding))
(: empty-env (-> Env))
(define empty-env
(lambda () '()))
(: extend-env (-> Symbol Any Env Env))
(define extend-env
(lambda (var val env)
(cons (cons var val) env)))
;; made up so the below will compile
(define None (lambda () 'None))
(: apply-env (-> Env Symbol Any))
(define apply-env
(lambda (env search-var)
(if (null? env)
(None)
(let ([saved-var (caar env)]
[saved-val (cdar env)])
(if (eqv? search-var saved-var)
saved-val
(apply-env (cdr env) search-var))))))

how to use map append correctly with lists of list in Racket

i have this code :
(: min&max-lists : (Listof (Listof Any)) -> (Listof Any))
(define (min&max-lists lst)
(apply append
(map (lambda (slst)
(sublist-numbers slst) ;; **
(cons (sublist-numbers slst) '()) ;; *
)
lst)))
if I run this code with the line with two ** everything works fine, but when I run the code above with the line with one * it shows me like that :
Type Checker: type mismatch
expected: (Listof Any)
given: Any in: slst
how can I solve this? I see if i use the slst with another function it shows me that it is typed if Any and they need List of Any but if I use it like with two ** it works fine
The problem with (cons (sublist-numbers slst) '()):
(cons x '()) ;; => '(x)
;; which is nothing other than:
(list x)
If x is already a list - like in this case)
you have instead of '(1 2 3) one list layer around it: '((1 2 3)).
If you apply-append such results you get (Listof (Listof Any)) instead of just (Listof Any).
So, if you want to run using the line *, you have to change the typing to:
(: min&max-lists : (Listof (Listof Any)) -> (Listof (Listof Any)))

Going over a list of strings to find the string with the needed prefix

I need to define the function plPrefixContained – that consumes 5 strings and
returns the first one that contains the string "pl" as a prefix – if one such
exists, and returns #f otherwise.
What I'm trying to do is to use the prefixes function to go over all the strings in the list and check their prefixes, put them in a new list and to output the first string as the result.
(I will handle the #f case later) my code is down below but it keeps giving me the error-
first: contract violation
expected: (and/c list? (not/c empty?))
given: '()
any help would be appreciated
(: plPrefixContained : String String String String String -> String)
(define (plPrefixContained x y z w v)
(list-ref (prefixes (list x y z w v) '()) 0))
(: prefixes : (Listof String) (Listof String) -> (Listof String))
(define (prefixes lis em)
(cond
[(and (eq? (string-ref (first lis) 0) "p") (eq? (string-ref (first lis) 1) "l"))
(cons (first lis) em)]
[else
(prefixes (rest lis) em)]))
this is how I want my output to be like-for example
(test (plPrefixContained "yypl" "opl" "lpTT" "plpl" "lol")
=>
"plpl")
There are two problems:
intensional equality eq?, instead of extensional equality such as equal? or string=?
comparing string / char, instead of comparing char / char or string / string
You are using eq?, which always makes me suspicious. eq? uses "intensional" equality, which is basically pointer equality, meaning that a string which is allocated somewhere in memory won't necessarily be eq? even if it has the same characters. You can see this with (eq? "abc123" (string-append "abc" "123")).
If you're dealing with strings, lists, or any other data which "contains" things, you should avoid eq?. Instead you should use an "extensional" equality predicate such as equal?, or even better, a predicate specific to the types of values you expect, such as string=?. Here's how they behave better than eq?:
> (eq? "abc123" (string-append "abc" "123"))
#f
> (equal? "abc123" (string-append "abc" "123"))
#t
> (string=? "abc123" (string-append "abc" "123"))
#t
Since you're comparing using the strings "p" and "l", I should be able to substitute eq? with string=? in your code:
(: prefixes : (Listof String) (Listof String) -> (Listof String))
(define (prefixes lis em)
(cond
[(and (string=? (string-ref (first lis) 0) "p") (string=? (string-ref (first lis) 1) "l"))
(cons (first lis) em)]
[else
(prefixes (rest lis) em)]))
However, this reveals the second problem, which I only spotted after seeing the error message:
string=?: contract violation
expected: string?
given: #\y
argument position: 1st
other arguments...:
"p"
The string=? isn't working because its first argument, the result of string-ref, is a character (like #\y), not a string. To fix this, use char=? instead of string=?, and compare with the characters #\p and #\l instead of the strings "p" and "l".
(: prefixes : (Listof String) (Listof String) -> (Listof String))
(define (prefixes lis em)
(cond
[(and (char=? (string-ref (first lis) 0) #\p) (char=? (string-ref (first lis) 1) #\l))
(cons (first lis) em)]
[else
(prefixes (rest lis) em)]))

Defining a function that accepts a List of Lists in racket

My assignment is to count how many lists I have with length 3 in my list (List of List).
I thought I built everything correctly, but when I want to send the first list to my recursive function it fails because my list has the type Any, and I can't find a way to make it a list of lists.
#lang pl
(: count-3lists : (Listof Any) -> Number)
(define (count-3lists l)
(cond
[(null? l) 0]
[else (+ (count-3lists-helper (first l)) (count-3lists (rest l)))]))
(: count-3lists-helper : (Listof Any) -> Number)
(define (count-3lists-helper l)
(cond [(= (length l) 3) 1]
[else 0]))
(: length : (Listof Any) -> Number)
(define (length l)
(cond
[(null? l) 0]
[else (add1 (length (rest l)))]))
The error I get is:
. Type Checker: Polymorphic function `first' could not be applied to
arguments:
Types: (Pairof a (Listof b)) -> (a : ((! False # (car) (0 0)) | (False # (car) (0 0))) : (car (0 0)))
(Listof a) -> a
Arguments: (Pairof Any (Listof Any))
Expected result: (Listof Any)
in: (first l)
It seems like you want your count-3lists function to take a list of lists as its input. Right now you have (Listof Any).
What you want is express something like (Listof List), but the inner list has to be a list of something, so you can write that as (Listof (Listof Any)).
Then the first part of your code becomes this:
(: count-3lists : (Listof (Listof Any)) -> Number)
(define (count-3lists l)
(cond
[(null? l) 0]
[else (+ (count-3lists-helper (first l)) (count-3lists (rest l)))]))
After that, the rest of your code works. It turns out that your length function was fine. (So you should probably rename your question.)

How to fix the return value in this typed racket code?

(: test (All (A) (-> A A (Rec Expr (Listof (U A Expr))) (Rec Expr (Listof (U A Expr))))))
(define (test new old expr)
(if (null? expr)
expr
(if (list? (car expr))
expr ; <- get error here
expr)))
get error
Type Checker: type mismatch
expected: (Rec g161252 (Listof (U A g161252)))
given: (Pairof
(U (Rec g161259 (Listof (U A g161259))) (Pairof Any (Listof Any)))
(Listof (U A (Rec g161268 (Listof (U A g161268)))))) in: expr
The code return exactly the same expr as the input one.
(define (test new old expr)
(if (and (not (null? expr)) (list? (car expr)))
expr ; <- also get the error
expr))
But
(define (test new old expr)
(if (and (list? (car expr)) (not (null? expr)))
expr ; <- this works fine
expr))
If the logic is in this order, then it works fine.
So why the type checker get the type mismatch error?
The problem with the original code is that expr is "not polymorphic enough". The query (list? (car expr)) changes the type of expr to something incompatible with the polymorphic A.
(It seems to me that you're trying to discriminate between an A and a nested Expr, but typed racket sees list? and refines the type of A. I think!)
Here's another function that's not polymorphic enough.
(: id (All (A) (-> A A)))
(define (id x)
(if (boolean? x) x x))
Fixes
If you're on an older version of Racket (v6.2) you can sneak around this with an alias, but that's not a nice thing to do.
(: id (All (A) (-> A A)))
(define (id x)
(define x2 x)
(if (boolean? x) x2 x)))
You can use list-ref instead of car, because list-ref doesn't allow predicates to affect its argument.
...
(if (list? (list-ref expr 0))
expr
expr)))
Change your types a little, so there's a clear way to tell apart A from Expr.
(: test (All (A) (-> A A (Rec Expr (Listof (U (Boxof A) Expr))) (Rec Expr (Listof (U (Boxof A) Expr))))))
(define (test new old expr)
(if (null? expr)
expr
(if (not (box? (car expr)))
expr
expr)))
Stop using Typed Racket's polymorphism -- it's too clumsy!
The order issue is because and applies predicates in order, and these predicates destructively change an expression's type. So testing (not (null? expr)) after you've checked (list? (car expr)) forgets that you ever did the first check.
Here's more code with the same issue. We ought to know that expr is non-null and has a list at its head, but Typed Racket's forgotten.
(: test2 (-> (Listof (U (List Natural) Boolean)) Boolean))
(define (test2 expr)
(if (and (list? (car expr)) (not (null? expr)))
(= 1 (car (car expr)))
#f))
This is probably a bug.