Here’s the most common words, first or otherwise: make 102
syntax 89
hash 62
string 60
module 58
current 56
bytes 56
in 46
path 42
read 38
Half refer to a data-type Care to share your script?
Will try to share tomorrow, heading to bed for now
I’m trying to pass a result of query-rows
from untyped racket to a function in my typed package. This functions takes a (Listof (Vectorof Any))
and processes it. But I get this error: any-wrap/c: Unable to protect opaque value passed as `Any`
Anyone knows how I should work around this?
My case is very similar to the one mentioned here: https://www.mail-archive.com/racket-users@googlegroups.com/msg32201.html
@hoshom in general using Any
is problematic — can you use a more specific type?
@343519265 has joined the channel
@hoshom The Any
inside the vector produced by query-rows
represents the things that could be in the database. Are those simple values like numbers or strings? Are they existing immutable racket data structures like lists? Are they existing mutable racket data structures like vectors or boxes? Or are they your own datatype that you’re defining to put in the database?
Or is it sql-null
that’s giving you the problem?
This is pretty clever: “Racket ABNF implementation (interpreter & compiler), for directly cutting-and-pasting from RFCs.”
@bennynguyensg has joined the channel
@samth can you make it so that people who aren’t admins can’t use @ everyone?
+1
I’ll see what I can do but slack moderation tools are not very good
I know, but I do know you can disable @ everyone
@samth @alexknauth I suppose I’ll post on the mailing list; slack doesn’t seem like a very good place for writing any amount of detail.
@hoshom it’s easier to discuss in real time here, though
oh ok
So yeah mostly the problem is sql-null
What started all this was, I had a type, mostly a union of primitives that I expected to deal with from the database. (U String Number Boolean Char Bytes)
. Two things that I wanted The moment I added SQL-Null
, all functions that dealt with these values started failing.
Though I think if it was an immutable vector instead of a mutable vector it would be fine
Because a mutable vector’s “contents” contract is used for both “typed -> untyped” crossings and “untyped -> typed” crossings
So what I did was this: (define-type RawResults (Listof (Vectorof Any)))
(define-type ResultRow (Vectorof AnyVal))
(define-type Results (Listof ResultRow))
(struct LocalSQLNull () #:prefab)
(define-type AnyVal
(U String
Number
Boolean
Char
Bytes
sql-timestamp
LocalSQLNull))
(: pre-process-results (-> RawResults Results))
(define (pre-process-results raw-res)
(map (λ([row-vec : (Vectorof Any)])
(vector-map (λ([val : Any])
(cond [(sql-null? val)
(LocalSQLNull)]
[else
(cast val AnyVal)]))
row-vec))
raw-res))
… And this works just fine in my test. My tests are in #lang typed/racket/base
, they use typed/rackunit
, and typed/db
, and I do not use query-rows
, I simply construct a list of vectors by hand, insert sql-null
values, and it all works.
But when I import this into an untyped module
and pass a result from query-rows
It complains about sql-null
This is a minimal, runnable example of the thing I’m trying to get around
I think you people are right and I should stop using Any
, ditch the LocalSQLNull
thing, remove RawResults
, use (Listof (Vectorof AnyVal))
, and use SQL-Null
from typed/db
, and try and fix the resulting type errors.
@hoshom I think that’s the right approach, but if it doesn’t work out let me know
and we should change sql-null
to be something that plays better with typed racket
@lexi.lambda I changed that setting
Thank you!
@samth Right, thanks, I just powered through all the type errors I was getting. The thing was, I had a lot of functions that relied on occurrence typing and they were all getting messed up. They were mostly working on a union of list, a custom struct, and an AnyVal
, where the else
case always applied to AnyVal
. And the list or custom struct could contain any of those 3 inside. But the moment I introduce SQL-Null
in the union type for AnyVal
they would all get messed up because the actual struct for sql-null
is hidden.
The way to work around this is to add a branch that checks for sql-null
before all the other branches, and simply copy whatever I was doing in the else
branches.
The code’s all here, in a very work-in-progress kinda package I’m making: https://gitlab.com/hashimmm/remap/blob/master/private/grouping-v2.rkt (though at the time of writing this, it doesn’t have the fixes/changes I’m making right now)
So when I was doing this, I did understand that, and I figured that if I can use the #:struct
form of require/typed
, it would work, and indeed, for sql-timestamp
, it all works, because if something is a list or a different struct it isn’t a sql-timestamp
and TR knows that. So it all works as expected.
At the time I looked up the code for sql-null
and found this had been done to make it a singleton. Wouldn’t a prefab struct achieve the same result, plus allow better interaction with TR?
A prefab struct wouldn’t make it a singleton
> (eq? #s(sql-null) #s(sql-null))
#f
Oh! totally did not expect that. My intuition failed me :disappointed:
the right thing from typed racket’s perspective would be to make it a transparent struct
I see
but that would allow people to violate the singleton invariant, maybe
I think I have a way to fix it, but I have to test it out
cool, I’ll check it out if I notice the change
Things mostly work now, but now my tests fail because check-equal?
complains for the exact same reason! check-equal?: contract violation
any-wrap/c: Unable to protect opaque value passed as `Any`
value: #<sql-null>
in: the 2nd argument of
(->* (Any Any) (any/c) any/c)
I think you want to import sql-null?
with #:opaque
to create a new type Sql-Null
, then import sql-null
with the type Sql-Null
.
(I haven’t read all the above context, so if that was already discussed, sorry.)
@hoshom unfortunately that’s a less-fixable problem with check-equal?
I think using check
and equal?
will work better
@samth Why does check-equal?
explode the any-wrap/c
contract? I don’t totally understand, but I’m curious. I thought any-wrap/c
just caused problems if you tried to use mutable state or call procedures or things like that.
@lexi.lambda it tries to wrap sql-null
with a chaperone that does the exploding, but it can’t (because it’s an opqaue struct) and so it errors
Ah, I see, that makes sense. Hence the “opaque” in the error message.
@samth oh, yes, that works. I’d given up for the time being, but, coool :blush:
@samth thanks!
Another thing we could do is improve vector contracts. Typed Racket relies on different contracts for Any
depending on whether its doing from “typed -> untyped” or “unyped -> typed”. The box/c
contract allows those to be different, so that different contracts can be applied depending on which direction its crossing the boundary in. If the vectorof
contract (and probably all the contracts for mutable data structures) allowed that in the same way, then any-wrap/c
would not be an issue in places like this.
(By that I mean, any-wrap/c
would only become an issue if you mutated it, but not if you only read from it)
@alexknauth I don’t really see how that would change things
any-wrap/c
doesn’t use box/c
or vectorof
I know, but the Vectorof
type uses the vectorof
contract
and the argument to vectorof
needs to be any-wrap/c
because of this
I’m confused
But if vectorof
took two contracts, one for “getting” and one for “setting”, then the “getting” one could be any/c
and only the “setting” one would have to be any-wrap/c
is the issue about using vectorof
in typed code?
Typed Racket’s contract-generation needs to be conservative to protect typed values
can you point to where in the code you’re talking about?
This distinction between getting and setting would allow Typed Racket to be more liberal on the getting side
I still don’t understand
I think Alex is talking about the place where untyped code calls pre-process-results
with a vector in @hoshom’s original example.
It wouldn’t solve the check-equal?
problem, but it would avoid the initial issue, if I understand what Alex is saying.
ah, I understand. You’re saying that (Vectorof Any)
could get translated to (vectorof any/c any-wrap/c)
Yes
Yes, where the “getting” side is #:covariant
and the “setting” side is #:contravariant
What’s any-wrap/c
? I sort of can’t find it
Is it an impersonator contract?
It’s sadly undocumented, I think… but it’s the contract TR uses for Any
(not any/c
).
Yes, it’s undocumented; first I looked in the docs, then I searched the github repo (racket/racket) and then I posted here. Is it an impersonator contract?
It’s a chaperone contract, I think.
hmm ok
I think this is the implementation if you’re interested, https://github.com/racket/typed-racket/blob/master/typed-racket-lib/typed-racket/utils/any-wrap.rkt
I’m basically trying to develop better intuition around what the error meant when it said “unable to protect opaque value passed as any”. Would it be correct to say that the error occurred because of all these things: (a) the function accepts a type name that TR doesn’t know about, (b) we gave TR a predicate, saying “this is how you check the type”, (c) in an untyped module, a value is given to that function that MAY be of the accepted type, (d) because the value is opaque, and the contract isn’t flat, TR cannot deduce whether the contract has modified the value, (e) as in, TR can’t tell that maybe the value was of the accepted type but the contract messed with it, or maybe the value was not of the accepted type but the contract may have made it so
(f) as in, the predicate may return true or false on that value, but that may not be the same value as the one originally passed, so the result of the predicate would be invalid
In which case, there seems to be a way out: because if the predicate is an “eq?” check, then the result of the predicate is always valid, no?
thanks! Silly me, I ought to have thought to search in the typed racket repo