First thing is you have to accept that you aren’t going to get the same thing as Typeable
. If you are looking to recover the same functionality as Typeable, you are going to be disappointed.
Typeable is “just” a unique hash of some type coupled with a coercion. This lets us defer our knowledge of some type to runtime, and otherwise completely forget about it, which can be really useful in frameworky stuff. Combined with GADTs, you can existentially pack up these hashes. There is no “safe” way to do exactly this in PureScript.
import Data.Leibniz (type (~))
newtype TypeRep = TypeRep String
derive instance eqTypeRep :: Eq TypeRep
class NaiveTypeable a where
typeRep :: a -> TypeRep
eqT :: forall a b. NaiveTypeable a => NaiveTypeable b => a -> b -> Maybe (a ~ b)
eqT a b
| typeRep a == typeRep b = Just (unsafeCoerce identity)
| otherwise = Nothing
data Foo
instance typeableMyFoo :: NaiveTypeable Foo where
typeRep _ = TypeRep "Data.Foo#Foo"
The whole reason Typeable is only compiler derived is because it relies on the user constructing their own hash, which is inherently unsafe.
So how does Variant solve some of the same problems? It doesn’t. Or at least not, directly. Any alternative to Typeable is to “just” not forget about the types. If you write everything over a unified closed sum, then you always know what everything can be. However this means the user has to write some larger data type to encompass all the cases they want in their program, which isn’t very modular. Variant lets one use polymorphic sums, which means you can define different branches in a fairly modular way, and have them unify when you use them together. So Variant does not solve the Typeable problem, but it makes the case of not having Typeable much easier to deal with, by not have to maintain a monolithic closed sum.
In the case of something like extensible effects, Typeable is used to implement an open union, where types can exist together, discriminated by their TypeRep. Variant and row-types solve a subset of that problem in a way that is mostly ergonomic and fully inferrable.
I can’t speak directly to your problem because it isn’t complete (you’ve brought up an XY problem). You’ve proposed a situation that is only solved by Typeable, and then asked how things that are not Typeable solve it. The question to ask at that point is, “How would I solve this with only ADTs and no Typeable?”, which is likely to be unergonomic, but can likely be made a lot nicer with the alternatives.