Open tekknolagi opened 10 months ago
Okay, I'll put some design thoughts here before I rush into another PR :)
First, after a bit of thought, I like the idea of calling these things Tags (short for "tagged unions" and nice mnemonic with the octothorp/hashtag). Let me know if that's palatable for y'all.
Next thing. I want to Tags always to hold exactly one value, BUT I want it syntactically to default to a Hole ()
if not provided. In practice, it'll look like this:
()
. _ = 7 == flavor-score flavors1::chocolate
-- defaults to hole on matching too
. flavor-score =
| #vanilla -> 9
| #chocolate -> 7
| #strawberry -> 3
-- these are semantically equivalent
. flavors1 : #vanilla #chocolate #strawberry
. flavors2 : #vanilla () #chocolate () #straberry ()
. _ = p1::point { x = 2, y = 6 } |> | #point { x, y } -> y
-- use records or pairs for multiple args
. p1 : #point { x : int, y : int }
. p2 : #point (pair int int)
-- a generic pair could look like this
. pair : a => b => #pair { x : a, y : b }
I'd like to make these things structurally transparent, so the following should technically work:
()
-- specifying a type annotation to restrict usage
. h : t1 =
| #a _ -> ()
| #b _ -> ()
-- good: g t1
-- good: g t2
-- type error: g t3
. g =
| #a _ -> ()
| #b _ -> ()
| #c 3 -> ()
| #c _ -> ()
-- type error: f t1
-- type error: f t2
-- type error: f t3
. f =
| #a _ -> ()
. t1 : #a int #b int
. t2 : #b int #c int
. t3 : #b int #c text
In order to make all of this work, the following structure seems like a good start:
class Tags(Obj):
value: Obj | None
type: dict[string, Type]
We might also have to have a Tag
class for the matching syntax.
Let me know what y'all think!
Ah, clarifying point on implementation:
-- { value: null, type: { a: "int", b: "int" } }
. _ = (#a int #b int)
-- { value: { a: 1 }, type: { a: "int", b: "int" } }
. _ = (#a int #b int)::a 1
Seems reasonable, I think. We already have Symbol
in case you want to rename that to Tag
or something. It's there to be the boolean tagged union
Are you planning on making the type errors run-time type errors?
Also: you want to add nulls?
Perfect, I'll take Symbol
then.
The type errors should eventually be compile-time type errors. I don't think we have a type-checker yet though, so would you like me to go down that rabbit-hole first?
No nulls! haha. The reason I have a null there is to distinguish a "loaded" Tags vs. "unloaded" Tags. The object carries the value as well as its type information.
-- UNLOADED
-- Tags( value: null, type: { a: "int", b: "int" } )
. _ = (#a int #b int)
-- LOADED
-- Tags( value: { a: 1 }, type: { a: "int", b: "int" } )
. _ = (#a int #b int)::a 1
Simple variants added in #123 -- but no compile-time checks, type annotations, etc
Great! I have been swamped lately -- I'm going to close this PR for now
Oops, I need to close my PR, not this issue haha