How would I make a struct that only takes a value if it's a boolean
(struct mystruct [val] //only take val if it's a boolean )
There are at least 3 ways:
Define the struct with a contract, using define-struct/contract.
Define the struct with a guard, using struct with #:guard.
a) A guard that uses a contract with struct-guard/c.
b) A guard manually defined with unless, boolean?, raise-argument-error.
Write your program in Typed Racket instead of Racket, and use a type like Boolean.
1: Struct with a contract
You can use define-struct/contract like this:
(define-struct/contract mystruct ([val boolean?]))
Using it, correctly and incorrectly:
> (define xt (mystruct #true))
> (define xf (mystruct #false))
> (mystruct-val xt)
#true
> (mystruct-val xf)
#false
> (mystruct 3)
make-mystruct: contract violation
expected: boolean?
given: 3
in: the 1st argument of
(-> boolean? symbol? any)
contract from: (struct mystruct)
blaming: .../so-54901923.rkt
(assuming the contract is correct)
2: Struct with a guard
You can use the #:guard keyword of struct like this:
(struct mystruct [val]
#:guard <guard-procedure>)
The easiest way to define a guard-procedure is with a library function like struct-guard/c on the Racket HEAD snapshot (not in 7.2, to be released in the next version of Racket).
(struct mystruct [val]
#:guard (struct-guard/c boolean?))
Using it, correctly and incorrectly:
> (define xt (mystruct #true))
> (define xf (mystruct #false))
> (mystruct-val xt)
#true
> (mystruct-val xf)
#false
> (mystruct 3)
mystruct: contract violation
expected: boolean?
given: 3
in: boolean?
contract from:
.../so-54901923.rkt
blaming: .../so-54901923.rkt
(assuming the contract is correct)
If something like struct-guard/c is unavailable to you, or if you need more power/control than struct-guard/c provides, you can define the guard-procedure manually. If the struct has n fields, the guard-procedure should take n + 1 arguments, corresponding to the fields plus a struct name.
For example, for your mystruct, it should be a function that takes the value of the val field and a name:
(lambda (val name)
???)
A guard-procedure should return a multiple-value result using n values, corresponding to the fields.
(lambda (val name)
(values val))
And it can run arbitrary checks on the values before passing them through:
(lambda (val name)
(unless (boolean? val)
(raise-argument-error name "boolean" val))
(values val))
Putting it as the #:guard:
(struct mystruct [val]
#:guard (lambda (val name)
(unless (boolean? val)
(raise-argument-error name "boolean" val))
(values val)))
Using it:
> (define xt (mystruct #true))
> (define xf (mystruct #false))
> (mystruct-val xt)
#true
> (mystruct-val xf)
#false
> (mystruct 3)
mystruct: contract violation
expected: boolean
given: 3
3: Typed racket
You can use #lang typed/racket. Its struct form has a type to every field:
#lang typed/racket
(struct mystruct ([val : Boolean]))
Using it:
> (define xt (mystruct #true))
> (define xf (mystruct #false))
> (mystruct-val xt)
- : Boolean
#true
> (mystruct-val xf)
- : Boolean
#false
> (mystruct 3)
Type Checker: type mismatch
expected: Boolean
given: Positive-Byte in: 3
Have you tried?
#lang typed/racket
(struct point ([q : Boolean] [x : Real] [y : Real]))
http://docs.racket-lang.org/ts-guide/quick.html#%28part..Using.Typed_.Racket_from_the_.Racket_.R.E.P.L%29
Related
I'm trying to load and use a function from a different module at run-time. The issue is that dynamic-require's range, Any, can't seem to be casted to a more specific (function) type.
test.rkt:
#lang typed/racket
(module other-module typed/racket
(provide f)
(: f : Integer -> Integer)
(define (f x)
(* x 2)))
; g has type Any because dynamic-require returns a value of type Any
(define g (dynamic-require '(submod "test.rkt" other-module) 'f))
;contract violation
; Attempted to use a higher-order value passed as `Any` in untyped code: #<procedure:f>
; in: Any
; contract from: typed-world
; blaming: cast
; (assuming the contract is correct)
((cast g (-> Integer Integer)) 3)
Is there any way to load and use a function at run-time from a different module in #lang typed/racket?
One work-around is to do the loading in an untyped module and use require/typed to assign types:
#lang typed/racket
(module other-module typed/racket
(provide f)
(: f : Integer -> Integer)
(define (f x)
(* x 2)))
(module another-module racket
(define g (dynamic-require '(submod "test.rkt" other-module) 'f))
(provide g))
(require/typed 'another-module
(g (-> Integer Integer)))
(g 3)
;; 6
But yeah, it would be nicer if dynamic-require could take a target type or Typed Racket allowed untyped regions (the opposite of with-type).
I'm trying to define a contract for a struct which inherits from another struct.
#lang racket
(require racket/contract racket/contract/parametric)
(struct semigroup (op))
(struct monoid (mempty) #:super struct:semigroup)
(define (semigroup/c a) (struct/dc semigroup [op (-> a a a)]))
(define (monoid/c a)
(struct/dc monoid [mempty a] [(op #:parent semigroup) (-> a a a)]))
I get an error
struct/dc: expected an identifier that names a field or a sequence with a field name, the #:parent keyword, and the parent
struct
at: (op #:parent semigroup)
in: (struct/dc monoid (mempty a) ((op #:parent semigroup) (-> a a a)))
I fail to see what I'm doing wrong.
I'm using Racket 6.9.
Suppose I want to convert the following untyped code into typed racket. These functions are inspired by SICP where they show how a data structure can be constructed purely from functions.
(define (make-pair x y)
(lambda (c)
(cond
((= c 1) x)
((= c 2) y)
(error "error in input, should be 1 or 2"))))
(define (first p) (p 1))
(define (second p) (p 2))
To convert it straight to typed racket, the return value of the make-pair function seems to be (: make-pair (All (A B) (-> A B (-> Number (U A B))))). And following this, the type of first should be (: first (All (A B) (-> (-> Number (U A B)) A))). However, while implementing the function we can't call (p 1) directly now because we need some sort of occurrence typing to make sure first returns only of type A. Changing the return type of first to (U A B) works but then the burden of occurrence typing goes on the user and not in the API. So in this scenario how can we use occurrence typing inside first (that is, how to use a predicate for type variable A) so that we can safely return only the first component of the pair?
UPDATE
I tried an approach which differs a bit from above and requires the predicates for A and B to be supplied as arguments to make-pair function. Below is the code:
#lang typed/racket
(define-type FuncPair (All (A B) (List (-> Number (U A B)) (-> A Boolean) (-> B Boolean))))
(: make-pair (All (A B) (-> A B (-> A Boolean) (-> B Boolean) (FuncPair A B))))
(define (make-pair x y x-pred y-pred)
(list
(lambda ([c : Number])
(cond
((= c 1) x)
((= c 2) y)
(else (error "Wrong input!"))))
x-pred
y-pred))
(: first (All (A B) (-> (FuncPair A B) Any)))
(define (first p)
(let ([pair-fn (car p)]
[fn-pred (cadr p)])
(let ([f-value (pair-fn 1)])
(if (fn-pred f-value)
f-value
(error "Cannot get first value in pair")))))
However, this fails in the check (fn-pred f-value) condition with error expected: A
given: (U A B) in: f-value
From the untyped code at the start of your question, it seems like a pair of A and B is a function that given 1, gives back A, and given 2, gives back B. The way to express this type of function is with a case-> type:
#lang typed/racket
(define-type (Pairof A B)
(case-> [1 -> A] [2 -> B]))
The accessors can be defined the same way as your original untyped code, just by adding type annotations:
(: first : (All (A B) [(Pairof A B) -> A]))
(define (first p) (p 1))
(: second : (All (A B) [(Pairof A B) -> B]))
(define (second p) (p (ann 2 : 2)))
The type of the constructor should be:
(: make-pair : (All (A B) [A B -> (Pairof A B)]))
But the constructor doesn't quite work as-is. One thing wrong with it is that your else clause is missing the else part of it. Fixing that gives you:
(: make-pair : (All (A B) [A B -> (Pairof A B)]))
(define (make-pair x y)
(lambda (c)
(cond
[(= c 1) x]
[(= c 2) y]
[else (error "error in input, should be 1 or 2")])))
This is almost right, and if typed racket were awesome enough, it would be. Typed racket treats equal? specially for occurrence typing, but it doesn't do the same thing for =. Changing = to equal? fixes it.
(: make-pair : (All (A B) [A B -> (Pairof A B)]))
(define (make-pair x y)
(lambda (c)
(cond
[(equal? c 1) x]
[(equal? c 2) y]
[else (error "error in input, should be 1 or 2")])))
Ideally occurrence typing should work with =, but perhaps the fact that things like (= 2 2.0) return true makes that both harder to implement and less useful.
I have a method which returns class definitions:
(define (max-tracker%)
(let ([current-maximum 0])
(class object%
(init val) ; <--
...
(define held-value 0)
(set-val val)
(define/public (set-val newval) ; <--
(when (newval . >= . current-maximum)
(set! current-maximum newval))
(set! held-value newval))
...
)))
how do I bind a contract to the set-val method?
You can use the with-contract form, which lets you create arbitrary contract regions in expressions:
(define (max-tracker%)
(with-contract
max-tracker-procedure
#:result contract-expr
(class object% (init val) ...)))
This creates a contract region named max-tracker-procedure that exports exactly one anonymous value that must adhere to the contract specified in contract-expr. In this case you could specify a class/c contract. Multiple values and exports can be specified too, by using Racket's ability to return multiple values and using #:results (contract-expr ...) instead. Here's a simpler example demonstrating this:
(define test-value
(with-contract test
#:result (or/c integer? symbol?)
"neither int nor symbol - should break contract"))
Running this should give you:
broke its contract:
promised: (or/c integer? symbol?)
produced: "neither int nor symbol - should break contract"
in: (or/c integer? symbol?)
contract from: (region test)
blaming: (region test)
Combining this with class/c should give you what you're looking for.
EDIT
Here's an example closer to what you'd like:
(define (with-greeting class%)
(with-contract with-greeting
#:result (class/c [greet (->m string? string?)])
(class class%
(super-new)
(define/public (greet person)
(string-append "Hello, " person "!")))))
(define simple-greeter% (with-greeting object%))
(define simple-greeter (new simple-greeter%))
(send simple-greeter greet "Jack")
(send simple-greeter greet 'Jack)
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.