Hi Crystal folks, I’ve just published an article on how to use the json
package for encoding and decoding algebraic data types.
This covers
- Automatic encoding with
JSON::Serializable
- Type resolution with discriminators
- Encoding of nested composite data types
- Considerations on the extensibility of this approach
Let me know how you find this, and tell your friends!
14 Likes
Really nice article!
How do you handle each event later on? Using a case, or method overloading, or something else?
I ask because a type hierarchy is not quite an algebraic data type. The reason is that in other languages you can pattern match against an algebraic type and make sure you cover all cases. Because inheritance is open you can’t do that in Crystal: someone might extend Event and add yet another Event that you didn’t think about.
I’d really like to add a Sealed attribute similar to what you can do in Sorbet, Kotlin and I think other languages, but others don’t seem to like or understand its usefulness.
4 Likes
Yes, I use a case
(here is the actual code), something like:
case event
when Initialized
total = event.total
todo = event.todo
name = event.name
when Connected
peer_data[event.peer] = PeerStatus.new(event.peer.address, :connected)
end
I ask because a type hierarchy is not quite an algebraic data type. The reason is that in other languages you can pattern match against an algebraic type and make sure you cover all cases
I see what you mean. Would switching to an exhaustive case
make this feel more like an ADT? Or would we still missing out on some properties?
I’d really like to add a Sealed attribute similar to what you can do in Sorbet, Kotlin and I think other languages, but others don’t seem to like or understand its usefulness.
I come from Scala’s sealed traits, so you have my full support on this
2 Likes
Could exhaustive case
exist at the language level? Like a modifier over case
?
I understand introducing keywords are frowned upon so maybe final
could have some meaning there. Or an entirely new feature with a more general syntax for extending some constructs with modifiers/attributes:
case(exhaustive, other_modifier) event
case ::exhaustive, other_modifier:: event
Thoughts?
Was it ever considered to replace the case
keyword instead of when
for exhaustiveness checking? For example,
match foo
when Bar
...
end
It’s a minor thing, but it would require a single change if you want to enable/disable exhaustiveness checks, instead of a change for each branch.
1 Like
Yes, it was considered. The match proposal is a breaking change, case in is not.
1 Like
While I appreciate the availability of the exhaustive case, even undocumented, I think there is a case to be made for attributes/modifiers in general. Even if I don’t have handy examples right now, I figure an extensibility mechanism is itself laudable and use cases will pop left and right the moment such a functionality is made available. That, along with the macro system, should enable some wicked use cases. Not all attributes have to be built in as long as there are ways to create those behaviour wrappers.
And… Aspects. Cross-cutting concerns are a thing. While I’m not fond of pointcut expressions, I think I can live with attributes to import aspects.
1 Like