this example shows (with a very very restrictive "sexp?" that actually only greenlights symbols), user-defined predicates don't seem to work in the same way as built-in ones like 'symbol?'.
I think the problem that your sexp?
can't work like symbol?
may be related to andmap
.
Welcome to Racket v8.4 [cs].
> (: sexp? (Any -> Boolean : #:+ Sexp))
> (define (sexp? s) (symbol? s))
> (: sexps? ((Listof Any) -> Boolean : #:+ (Listof Sexp)))
> (define (sexps? s) ((inst andmap Any Boolean Sexp) sexp? s))
string:1:19: Type Checker: type mismatch;
mismatch in proposition
expected: ((: s (Listof Sexp)) | Top)
given: (Top | Top)
in: ((inst andmap Any Boolean Sexp) sexp? s)
[,bt for context]
>
TR seems to have special handling for the type of andmap
, for example, we can define listof?
directly through andmap
:
Welcome to Racket v8.4 [cs].
> (: listof? (All (A) (case-> [-> (pred A) (pred (Listof A))]
[-> [-> Any Boolean] [-> Any Boolean]])))
> (define listof?
(λ (pred)
(λ (arg)
(and (list? arg)
(andmap pred arg)))))
> (:print-type listof?)
(All (A)
(case->
(-> (-> Any Boolean : A) (-> Any Boolean : (Listof A)))
(-> (-> Any Boolean) (-> Any Boolean))))
> (:print-type andmap)
(All (a c d b ...)
(case->
(-> (-> a c : d) (Listof a) c)
(-> (-> a b ... b c) (Listof a) (Listof b) ... b (U True c))))
>
But if we manually retype andmap
, this code will throw an error:
Welcome to Racket v8.4 [cs].
> (require typed/racket/unsafe)
> (unsafe-require/typed racket/base
[andmap
(All (a c d b ...)
(case->
(-> (-> a c : d) (Listof a) c)
(-> (-> a b ... b c) (Listof a) (Listof b) ... b (U True c))))])
> (: listof? (All (A) (case-> [-> (pred A) (pred (Listof A))]
[-> [-> Any Boolean] [-> Any Boolean]])))
> (define listof?
(λ (pred)
(λ (arg)
(and (list? arg)
(andmap pred arg)))))
string:5:11: Type Checker: Polymorphic function `andmap2' could not be applied to arguments:
Types: (-> a c : d) (Listof a) -> c
(-> a b ... b c) (Listof a) (Listof b) ... b -> (U True c)
Arguments: (-> Any Boolean : A) (Listof Any)
Expected result: Boolean
in: (andmap pred arg)
[,bt for context]
> (:print-type andmap)
(All (a c d b ...)
(case->
(-> (-> a c : d) (Listof a) c)
(-> (-> a b ... b c) (Listof a) (Listof b) ... b (U True c))))
>
In my case, I don't care about fancy things, I only want (what I believe to be) some pretty simple things, so I can possibly "roll my own" sexp?
I don't think there's a safe way to implement sexp?
in TR (and I'm not sure why you want only greenlights symbols predicates), I wonder if this weaker sexp?
can solve the problem you met?
(define-type S-Exp (U Boolean Real Char String Bytes
Keyword Symbol (Listof S-Exp)))
(define-predicate sexp? S-Exp)
it requires a chaperone (apologies if I'm saying that incorrectly), so there's no flat sexp? check.
When I want to deal with mutable types, I'm always frustrated that I can't refine their types through predicates. But I thought before that maybe we could tag these non-flat values through contract and then get their original type information (see [Feature Request] Tag Contract and Tag Type - #3 by NoahStoryM).
I don't know much about the specific implementation of contract and type, so I'm not sure whether tag type is feasible or not, hope this idea can help you.