Why does `filter` work with higher-order occurrence typing? - racket

On the homepage for Racket, they show this example:
#lang typed/racket
;; Using higher-order occurrence typing
(define-type SrN (U String Number))
(: tog ((Listof SrN) -> String))
(define (tog l)
(apply string-append (filter string? l)))
(tog (list 5 "hello " 1/2 "world" (sqrt -1)))
I know that with occurrence typing, an expression like (if (string? v) ...) will mark v as having type String in the true branch. This is because the type of string? has a filter on it:
> string?
- : (Any -> Boolean : String)
#<procedure:string?>
So, for "higher-order occurrence typing" to work with the filter function, I would expect the type of filter to say that a filter on the predicate's type gets propagated to the result type. But when I check the type of filter in the REPL, this doesn't show up:
> filter
- : (All (a b) (case-> ((a -> Any) (Listof a) -> (Listof b)) ((a -> Any) (Listof a) -> (Listof a))))
#<procedure:filter>
But this can't be the real type of filter, because there's no constraint on b! I expected something like
(All (a b) (a -> Any : b) (Listof a) -> (Listof b))
tldr: Why does filter appear have an unconstrained type variable in its return type?
EDIT: I am using Racket v6.0

In the Typed Racket Reference it is said:
In some cases, asymmetric type information is useful in filters. For example, the filter function’s first argument is specified with only a positive filter.
Example:
> filter
- : (All (a b)
(case->
(-> (-> a Any : #:+ b) (Listof a) (Listof b))
(-> (-> a Any) (Listof a) (Listof a))))
#<procedure:filter>
The use of #:+ indicates that when the function applied to a variable evaluates to a true value, the given type can be assumed for the variable. However, the type-checker gains no information in branches in which the result is #f.
In other words, if you have the following example:
> (filter string? '(symbol 1 2 3 "string"))
- : (Listof String)
'("string")
the system can says that an element of the list '(symbol 1 2 3 "string") is of type String only when string? returns true on it. Only in this case the type of the filter's predicate is propagated.

Related

What does `n` mean in Typed Racket?

n shows up at the type level in error messages, what is it?
Example:
(: trick (All (t u) ((U t (Listof u)) -> (Listof u))))
(define (trick x)
(cond
[(list? x) x]
)
)
Error with this n type:
Type Checker: type mismatch
expected: (Listof u)
given: (U (Listof u) (∩ (Pairof Any (Listof Any)) t)) in: x
I tried writing a type signature with n in it and using go-to-definition in Dr. Racket, but it says the symbol is not found.
it's not n, it's ∩ (intersection).
Thanks #willness and #molbdnilo.

Understanding typechecker behavior around `case->` [duplicate]

For example, how can I write a version of map that will work with polymorphic functions in Typed Racket? I use a simple id function defined as:
(: id : (All (A) A -> A))
(define (id x) x)
When I try to map it over a list i get an error:
> (map id '(1 2 3))
Type Checker: Polymorphic function `map' could not be applied to arguments:
Types: (-> a b ... b c) (Listof a) (Listof b) ... b -> (Listof c)
(-> a c) (Pairof a (Listof a)) -> (Pairof c (Listof c))
Arguments: (All (A) (-> A A)) (List One Positive-Byte Positive-Byte)
Expected result: AnyValues
in: (map id (quote (1 2 3)))
You have to manually instantiate the polymorphism in this case:
-> (map (inst identity Integer) '(1 2 3))
- : (Listof Integer) [more precisely: (Pairof Integer (Listof Integer))]
'(1 2 3)
The reason is explained in the Typed Racket Guide here:
Typed Racket’s local type inference algorithm is currently not able to
infer types for polymorphic functions that are used on higher-order
arguments that are themselves polymorphic.
(see docs for more explanation and examples)

How to apply in typed/racket?

In typed/racket I have a case like [(? procedure? p ) (apply p xv*)]
It will cause error:
Type Checker: Function has no cases in: (apply p xv*)
So I write a test case to detect the reason:
#lang typed/racket
(: test-match-apply-0 (-> (-> Any * Any) (Listof Any) Any))
(define test-match-apply-0
(lambda (x args)
(match x
[(? procedure? p) (apply p args)])))
;; Type Checker: Function has no cases in: (apply p args)
(test-match-apply-0 + (list 1 2 3)) ;; not ok
(apply + (list 2 4)) ;; ok
(: test-match-apply-1 (-> (-> (Listof Any) Any) (Listof Any) Any))
(define test-match-apply-1
(lambda (x args)
(match x
[(? procedure? p) (apply p args)])))
(test-match-apply-1 + (list 1 2 3)) ;; not ok
;; For int is it right
(: test-match-apply-2 (-> (-> (Listof Any) Any) (Listof Number) Number))
(define test-match-apply-2
(lambda (x args)
(match x
[(? procedure? p) (apply p args)])))
(test-match-apply-2 + (list 1 2 3)) ;; not ok
(: test-match-apply-3 (-> (-> Number * Number) (Listof Number) Number))
(define test-match-apply-3
(lambda (x args)
(match x
[(? procedure? p) (apply p args)])))
(test-match-apply-3 + (list 1 2 3)) ;; it is ok
I print the + itself:
> (:print-type +)
(case->
(-> Zero)
(-> Number Number)
(-> Zero Zero Zero)
(-> Number Zero Number)
(-> Zero Number Number)
(-> Positive-Byte Positive-Byte Positive-Index)
(-> Byte Byte Index)
(-> Positive-Byte Positive-Byte Positive-Byte Positive-Index)
(-> Byte Byte Byte Index)
(-> Positive-Index Index Positive-Fixnum)
(-> Index Positive-Index Positive-Fixnum)
(-> Positive-Index Index Index Positive-Fixnum)
(-> Index Positive-Index Index Positive-Fixnum)
(-> Index Index Positive-Index Positive-Fixnum)
(->* (Index Index) (Index) Nonnegative-Fixnum)
.....
Come back to my origin needs, How can I make it [(? procedure? p ) (apply p xv*)] possible in typed/racket? Because in the case I can't detect p 's type. Something like type-apply?
The reason Typed Racket can’t apply that procedure is because it knows nothing about it aside from the fact that it is a procedure. It might not take any arguments, for example, in which case that apply would cause a runtime error. It might take a different kind of argument, or it might even have required keyword arguments. TR doesn’t know any of this just from the procedure? predicate succeeding, so it doesn’t allow you to invoke such a value.
This is tricky, because there is no predicate that will allow you to inspect enough details about the function that will make it safe to apply. You basically have two options:
Constrain the type of the input so that procedure? will restrict it to a specific function type. You can do this by making the input a union of specific types. For example, this typechecks:
(: constrained ((U String Number (String * -> String)) -> String))
(define (constrained x)
(match x
[(? string?) x]
[(? number?) (number->string x)]
[(? procedure?) (apply x '("a" "b" "c"))]))
Even though the type is a union type here, since there is only one possible case for which the procedure? predicate is true, TR can restrict the type to a properly applicable value.
The type of the function itself can get pretty fancy, and TR can still figure it out. For example, it still works with a polymorphic type:
(: poly-constrained (All [a] (U String Number (a * -> String)) (Listof a) -> String))
(define (poly-constrained x lst)
(match x
[(? string?) x]
[(? number?) (number->string x)]
[(? procedure?) (apply x lst)]))
Alternatively, you can use cast. This will allow you to tell TR to perform a dynamic check that a value matches a particular type.
(: unconstrained (Any -> String))
(define (unconstrained x)
(match x
[(? string?) x]
[(? number?) (number->string x)]
[(? procedure?) (apply (cast x (String * -> String)) '("a" "b" "c"))]))
However, note that this is a little big dangerous! There are a couple pitfalls to using cast:
The check generates a typed/untyped boundary for a single value, effectively the same sort of boundary between typed and untyped modules. This means that cast generates a contract, which is checked at runtime, which, unlike static types, takes time and can reduce performance significantly if used in a tight loop.
Since cast performs the check dynamically, you lose one of the main benefits of Typed Racket: static type safety. If, for example, someone provides a procedure that does not match the given type, a runtime error will occur, which is precisely the sort of thing Typed Racket is designed to prevent.
If possible, you probably want to use the first approach so that you don’t compromise type safety, but in cases where predicates are not good enough, you can use cast. Just be aware of the downsides before you choose it.

Could not undersand racket function

My proffesor gave us this function:
(: every? : (All (A) (A -> Boolean) (Listof A) -> Boolean))
(define (every? pred lst)
(or (null? lst)
(and (pred (first lst))
(every? pred (rest lst)))))
I couldn't understand the meaning of: All (A) (A -> Boolean).
please can someone can explain to me - what is the meaning of the variable, what the function get, what is it do and what is it return because i can't figure it out.
Let's give the function every? a spin at the repl:
> (every? even? (list 1 2 3 4))
#f
> (every? char? (list #\a #\b #\c))
#t
Note that the type of the first list (list 1 2 3 4) is (Listof Number).
The type of the second list (list #\a #\b #\c) is (Listof Char).
What type should the lst argument of every? have?
Clearly it needs to be a list, but what type are the elements?
We don't know, so we make it a (Listof A), where A stands
for some (unknown) type.
However the predicate pred is called on the elements in the list,
so the type must match. In the first example: even? has the type
"function from number to boolean" aka (A -> Boolean).
In general we need the type: (A -> Boolean) for the predicate.
This becomes:
(: every? : (All (A) (A -> Boolean) (Listof A) -> Boolean))

Typed Racket: Creating generic types with define-type

I'm trying to get a bit into Typed Racket, but I'm having some trouble getting an (admittedly rather constructed) experiment to work.
This is what I originally had:
#lang typed/racket
(: generate-list
(All (A)
((A -> A) (Integer -> A) Integer -> (Listof A))))
(define (generate-list function location-function num-items)
(let: loop : (Listof A)
((count : Integer 0)
(result : (Listof A) (list)))
(if (>= count num-items)
(reverse result)
(loop (+ count 1)
(cons (function (location-function count)) result)))))
; ---------------------------------
(: f (Number -> Number))
(define (f x) (* x x))
(: locf (Integer -> Number))
(define (locf x) x)
; ---------------------------------
(displayln (generate-list f locf 10))
Which has the output:
(0 1 4 9 16 25 36 49 64 81)
Which is nice. Then I figured I could make this a bit better documented by giving the function and location-function a defined type:
#lang typed/racket
(define-type (ListGenFunction A) (A -> A))
(define-type (ListGenLocFunction A) (Integer -> A))
(: generate-list
(All (A)
(ListGenFunction ListGenLocFunction Integer -> (Listof A))))
(define (generate-list function location-function num-items)
(let: loop : (Listof A)
((count : Integer 0)
(result : (Listof A) (list)))
(if (>= count num-items)
(reverse result)
(loop (+ count 1)
(cons (function (location-function count)) result)))))
; ----------- Numbers! ------------
(: f ListGenFunction)
(define (f x) (* x x))
(: locf ListGenLocFunction)
(define (locf x) x)
; ---------------------------------
(displayln (generate-list f locf 10))
Now here's where the problems start (and I really hope some experienced Typed Racketeers aren't facepalming too hard right now). For one, the type checker gives me an error on the line where I define f. The message is rather lengthy, but it's basically: "Type Checker: No function domains matched in function application: Types: ... in: (* x x)". I thought I defined a type that has one parameter of a generic type A that returns a generic type A? Wouldn't (* x x) work? Or is there some need to "tag" the type? (Like in C++-like languages where it's list<int> for example)
On top of that: Now my type-definition for the function generate-list has a return type of "(Listof A)". But that A is not at all declared to be the same A that the parameters with the types ListGenFunction and ListGenLocFunction expect. I kind of want to make that connection, however, so that anyone who uses that function can be sure that the types of his provided functions match the type of the returning list items.
How do I do this correctly?
PS:
I'm not sure if I described my intention in the last paragraph so that anyone can understand it. But if you take some generic pseudo-C++-like code, I want to get the following:
list<T> generate-list(LGF<T> f, LGLF<T> locf, int count) { ... }
So that all T's are exactly the same.
There are two problems here, both of which stem from the same confusion. You're using a generic type, ListGenFunction, without telling Typed Racket (or the reader of your program) what particular type you're using it with.
For example, f isn't an arbitrary ListGenFunction, it's a ListGenFunction that works specifically on numbers. So you should write:
(: f (ListGenFunction Integer))
and
(: locf (ListGenLocFunction Integer))
Similarly, you should give generate-list a type like this:
(: generate-list
(All (A)
((ListGenFunction A) (ListGenLocFunction A) Integer -> (Listof A))))
This is just like how you explicitly say that you're producing a (Listof A), not just a Listof.