Resolving an ambiguous type variable - haskell

I have these two functions:
load :: Asset a => Reference -> IO (Maybe a)
send :: Asset a => a -> IO ()
The Asset class look like this:
class (Typeable a,ToJSON a, FromJSON a) => Asset a where
ref :: a -> Reference
...
The first reads an asset from disk, and the second transmits a JSON representation to a WebSocket. In isolation they work fine, but when I combine them the compiler cannot deduce what concrete type a should be. (Could not deduce (Asset a0) arising from a use of 'load')
This makes sense, I have not given a concrete type and both load and send are polymorphic. Somehow the compiler has to decide which version of send (and by extension what version of toJSON) to use.
I can determine at run time what the concrete type of a is. This information is actually encoded both in the data on the disk and the Reference type, but I do not know for sure at compile time as the type checker is being run.
Is there a way to pass the correct type at run time an still keep the type checker happy?
Additional Information
The definition of Reference
data Reference = Ref {
assetType:: String
, assetIndex :: Int
} deriving (Eq, Ord, Show, Generic)
References are derived by parsing a request from a WebSocket as follows where Parser comes from the Parsec library.
reference :: Parser Reference
reference = do
t <- string "User"
<|> string "Port"
<|> string "Model"
<|> ...
char '-'
i <- int
return Ref {assetType = t, assetIndex =i}
If I added a type parameter to Reference I simply push my problem back into the parser. I still need to turn a string that I do not know at compile time into a type to make this work.

You can't make a function that turns string data into values of different types depending on what is in the string. That's simply impossible. You need to rearrange things so that your return-type doesn't depend on the string contents.
Your type for load, Asset a => Reference -> IO (Maybe a) says "pick any a (where Asset a) you like and give me a Reference, and I'll give you back an IO action that produces Maybe a". The caller picks the type they expect to be loaded by the reference; the contents of the file do not influence which type is loaded. But you don't want it to be chosen by the caller, you want it to be chosen by what's stored on disk, so the type signature simply doesn't express the operation you actually want. That's your real problem; the ambiguous type variable when combining load and send would be easily resolved (with a type signature or TypeApplications) if load and send were individually correct and combining them was the only problem.
Basically you can't just have load return a polymorphic type, because if it does then the caller gets to (must) decide what type it returns. There's two ways to avoid this that are more-or-less equivalent: return an existential wrapper, or use rank 2 types and add a polymorphic handler function (continuation) as a parameter.
Using an existential wrapper (requires GADTs extension), it looks something like this:
data SomeAsset
where Some :: Asset a => a -> SomeAsset
load :: Reference -> IO (Maybe SomeAsset)
Notice load is no longer polymorphic. You get a SomeAsset that (as far as the type checker is concerned) could contain any type that has an Asset instance. load can internally use whatever logic it wants split into multiple branches and come up with values of different types of asset on different branches; provided each branch ends with wrapping up the asset value with the SomeAsset constructor all of the branches will return the same type.
To send it, you would use something like (ignoring that I'm not handling Nothing):
loadAndSend :: Reference -> IO ()
loadAndSend ref
= do Just someAsset <- load ref
case someAsset
of SomeAsset asset -> send asset
The SomeAsset wrapper guarantees that Asset holds for its wrapped value, so you can unwrap them and call any Asset-polymorphic function on the result. However you can never do anything with the value that depends on the specific type in any other way1, which is why you have to keep it wrapped up and case match on it all the time; if the case expression results in a type that depends on the contained type (such as case someAsset of SomeAsset a -> a) the compiler will not accept your code.
The other way is to instead use RankNTypes and give load a type like this:
load :: (forall a. Asset a => a -> r) -> Reference -> IO (Maybe r)
Here load doesn't return a value representing the loaded asset at all. What it does instead is take a polymorphic function as an argument; the function works on any Asset and returns a type r (that was chosen by load's caller), so again load can internally branch however it wants and construct differently-typed assets in the different branches. The different asset types can all be passed to the handler, so the handler can be called in every branch.
My preference is often to use the SomeAsset approach, but then to also use RankNTypes and define a helper function like:
withSomeAsset :: (forall a. Asset a => a -> r) -> (SomeAsset -> r)
withSomeAsset f (SomeAsset a) = f a
This avoids having to restructure your code into continuation passing style, but takes away the heave case syntax everywhere you need to use a SomeAsset:
loadAndSend :: Reference -> IO ()
loadAndSend ref
= do Just asset <- load ref
withSomeAsset send asset
Or even add:
sendSome = withSomeAsset send
Daniel Wagner suggested adding the type parameter to Reference, which the OP objected to by stating that simply moves the same problem to when the references are constructed. If the references contain data representing which type of asset they refer to, then I would strongly recommend taking Daniel's advice, and using the concepts described in this answer to address that problem at the reference-constructing level. Reference having a type parameter prevents mixing up references to the wrong types of assets where you do know the type.
And if you do significant processing with references and assets of the same type, then having the type parameter in your workhorse code can catch easy mistakes mixing them up even if you usually existential the type away at the outer levels of code.
1 Technically your Asset implies Typeable, so you can test it for specific types and then return those.

Sure, make Reference store the type.
data Reference a where
UserRef :: Int -> Reference User
PortRef :: Int -> Reference Port
ModelRef :: Int -> Reference Model
load :: Asset a => Reference a -> IO (Maybe a)
send :: Asset a => a -> IO ()
If necessary, you can still recover the strong points of your original Reference type by existentially boxing it.
data SomeAsset f where SomeAsset :: Asset a => f a -> SomeAsset f
reference :: Parser (SomeAsset Reference)
reference = asum
[ string "User" *> go UserRef
, string "Port" *> go PortRef
, string "Model" *> go ModelRef
]
where
go :: Asset a => (Int -> Parser (Reference a)) -> Parser (SomeAsset Reference)
go constructor = constructor <$ char '-' <*> int
loadAndSend :: SomeAsset Reference -> IO ()
loadAndSend (SomeAsset reference) = load reference >>= traverse_ send

After reviewing the answers from Daniel Wagner and Ben, I ultimately resolved my issue using a combination of the two which I place here in hopes it will aid others.
First, per Daniel Wagner's answer, I added a phantom type to Reference:
data Reference a = Ref {
assetType:: String
, assetIndex :: Int
} deriving (Eq, Ord, Show, Generic)
I chose not to use a GADT constructors and leave the string reference to assetType as I frequently send references over the wire and/or parse them from incoming text. I felt there were too many code points where I needed a generic reference. For those cases, I fill in the phantom type with Void:
{-# LANGUAGE EmptyDataDecls #-}
data Void
-- make this reference Generic
voidRef :: Reference a -> Reference Void
castRef :: a -> Reference b -> Reference a
-- ^^^ Note this can be undefined used only for its type
With this the load type signature becomes load :: Asset a => Reference a -> IO (Maybe a) So the Asset is always matches the type of the Reference. (Yay type safety!)
That still doesn't address how to load a generic reference. For those cases, I wrote some new code using the second half of Ben's answer. By wrapping the asset in SomeAsset, I can return a Type which is making the type checker happy.
{-# LANGUAGE GADTs #-}
import Data.Aeson (encode)
loadGenericAsset :: Reference Void -> IO SomeAsset
loadGenericAsset ref =
case assetType ref of
"User" -> Some <$> load (castRef (undefined :: User) ref)
"Port" -> Some <$> load (castRef (undefined :: Port) ref)
[etc...]
send :: SomeAsset -> IO ()
send (Some a) = writeToUser (encode a)
data SomeAsset where
Some :: Asset a => a -> SomeAsset

Related

Clarification on Existential Types in Haskell

I am trying to understand Existential types in Haskell and came across a PDF http://www.ii.uni.wroc.pl/~dabi/courses/ZPF15/rlasocha/prezentacja.pdf
Please correct my below understandings that I have till now.
Existential Types not seem to be interested in the type they contain but pattern matching them say that there exists some type we don't know what type it is until & unless we use Typeable or Data.
We use them when we want to Hide types (ex: for Heterogeneous Lists) or we don't really know what the types at Compile Time.
GADT's provide the clear & better syntax to code using Existential Types by providing implicit forall's
My Doubts
In Page 20 of above PDF it is mentioned for below code that it is impossible for a Function to demand specific Buffer. Why is it so? When I am drafting a Function I exactly know what kind of buffer I gonna use eventhough I may not know what data I gonna put into that.
What's wrong in Having :: Worker MemoryBuffer Int If they really want to abstract over Buffer they can have a Sum type data Buffer = MemoryBuffer | NetBuffer | RandomBuffer and have a type like :: Worker Buffer Int
data Worker x = forall b. Buffer b => Worker {buffer :: b, input :: x}
data MemoryBuffer = MemoryBuffer
memoryWorker = Worker MemoryBuffer (1 :: Int)
memoryWorker :: Worker Int
As Haskell is a Full Type Erasure language like C then How does it know at Runtime which function to call. Is it something like we gonna maintain few information and pass in a Huge V-Table of Functions and at runtime it gonna figure out from V-Table? If it is so then what sort of Information it gonna store?
GADT's provide the clear & better syntax to code using Existential Types by providing implicit forall's
I think there's general agreement that the GADT syntax is better. I wouldn't say that it's because GADTs provide implicit foralls, but rather because the original syntax, enabled with the ExistentialQuantification extension, is potentially confusing/misleading. That syntax, of course, looks like:
data SomeType = forall a. SomeType a
or with a constraint:
data SomeShowableType = forall a. Show a => SomeShowableType a
and I think the consensus is that the use of the keyword forall here allows the type to be easily confused with the completely different type:
data AnyType = AnyType (forall a. a) -- need RankNTypes extension
A better syntax might have used a separate exists keyword, so you'd write:
data SomeType = SomeType (exists a. a) -- not valid GHC syntax
The GADT syntax, whether used with implicit or explicit forall, is more uniform across these types, and seems to be easier to understand. Even with an explicit forall, the following definition gets across the idea that you can take a value of any type a and put it inside a monomorphic SomeType':
data SomeType' where
SomeType' :: forall a. (a -> SomeType') -- parentheses optional
and it's easy to see and understand the difference between that type and:
data AnyType' where
AnyType' :: (forall a. a) -> AnyType'
Existential Types not seem to be interested in the type they contain but pattern matching them say that there exists some type we don't know what type it is until & unless we use Typeable or Data.
We use them when we want to Hide types (ex: for Heterogeneous Lists) or we don't really know what the types at Compile Time.
I guess these aren't too far off, though you don't have to use Typeable or Data to use existential types. I think it would be more accurate to say an existential type provides a well-typed "box" around an unspecified type. The box does "hide" the type in a sense, which allows you to make a heterogeneous list of such boxes, ignoring the types they contain. It turns out that an unconstrained existential, like SomeType' above is pretty useless, but a constrained type:
data SomeShowableType' where
SomeShowableType' :: forall a. (Show a) => a -> SomeShowableType'
allows you to pattern match to peek inside the "box" and make the type class facilities available:
showIt :: SomeShowableType' -> String
showIt (SomeShowableType' x) = show x
Note that this works for any type class, not just Typeable or Data.
With regard to your confusion about page 20 of the slide deck, the author is saying that it's impossible for a function that takes an existential Worker to demand a Worker having a particular Buffer instance. You can write a function to create a Worker using a particular type of Buffer, like MemoryBuffer:
class Buffer b where
output :: String -> b -> IO ()
data Worker x = forall b. Buffer b => Worker {buffer :: b, input :: x}
data MemoryBuffer = MemoryBuffer
instance Buffer MemoryBuffer
memoryWorker = Worker MemoryBuffer (1 :: Int)
memoryWorker :: Worker Int
but if you write a function that takes a Worker as argument, it can only use the general Buffer type class facilities (e.g., the function output):
doWork :: Worker Int -> IO ()
doWork (Worker b x) = output (show x) b
It can't try to demand that b be a particular type of buffer, even via pattern matching:
doWorkBroken :: Worker Int -> IO ()
doWorkBroken (Worker b x) = case b of
MemoryBuffer -> error "try this" -- type error
_ -> error "try that"
Finally, runtime information about existential types is made available through implicit "dictionary" arguments for the typeclasses that are involved. The Worker type above, in addtion to having fields for the buffer and input, also has an invisible implicit field that points to the Buffer dictionary (somewhat like v-table, though it's hardly huge, as it just contains a pointer to the appropriate output function).
Internally, the type class Buffer is represented as a data type with function fields, and instances are "dictionaries" of this type:
data Buffer' b = Buffer' { output' :: String -> b -> IO () }
dBuffer_MemoryBuffer :: Buffer' MemoryBuffer
dBuffer_MemoryBuffer = Buffer' { output' = undefined }
The existential type has a hidden field for this dictionary:
data Worker' x = forall b. Worker' { dBuffer :: Buffer' b, buffer' :: b, input' :: x }
and a function like doWork that operates on existential Worker' values is implemented as:
doWork' :: Worker' Int -> IO ()
doWork' (Worker' dBuf b x) = output' dBuf (show x) b
For a type class with only one function, the dictionary is actually optimized to a newtype, so in this example, the existential Worker type includes a hidden field that consists of a function pointer to the output function for the buffer, and that's the only runtime information needed by doWork.
In Page 20 of above PDF it is mentioned for below code that it is impossible for a Function to demand specific Buffer. Why is it so?
Because Worker, as defined, takes only one argument, the type of the "input" field (type variable x). E.g. Worker Int is a type. The type variable b, instead, is not a parameter of Worker, but is a sort of "local variable", so to speak. It can not be passed as in Worker Int String -- that would trigger a type error.
If we instead defined:
data Worker x b = Worker {buffer :: b, input :: x}
then Worker Int String would work, but the type is no longer existential -- we now always have to pass the buffer type as well.
As Haskell is a Full Type Erasure language like C then How does it know at Runtime which function to call. Is it something like we gonna maintain few information and pass in a Huge V-Table of Functions and at runtime it gonna figure out from V-Table? If it is so then what sort of Information it gonna store?
This is roughly correct. Briefly put, each time you apply constructor Worker, GHC infers the b type from the arguments of Worker, and then searches for an instance Buffer b. If that is found, GHC includes an additional pointer to the instance in the object. In its simplest form, this is not too different from the "pointer to vtable" which is added to each object in OOP when virtual functions are present.
In the general case, it can be much more complex, though. The compiler might use a different representation and add more pointers instead of a single one (say, directly adding the pointers to all the instance methods), if that speeds up code. Also, sometimes the compiler needs to use multiple instances to satisfy a constraint. E.g., if we need to store the instance for Eq [Int] ... then there is not one but two: one for Int and one for lists, and the two needs to be combined (at run time, barring optimizations).
It is hard to guess exactly what GHC does in each case: that depends on a ton of optimizations which might or might not trigger.
You could try googling for the "dictionary based" implementation of type classes to see more about what's going on. You can also ask GHC to print the internal optimized Core with -ddump-simpl and observe the dictionaries being constructed, stored, and passed around. I have to warn you: Core is rather low level, and can be hard to read at first.

Enforce class constraints in type class that is not captured in the type signature of implementing type

I am trying to use a typeclass that enforces a constraint on the type returned by one of the functions it defines. But the return type of the function does not capture the constraint in its type variable. I would like to know what is wrong with the code or what is the correct way to encode it.
A sample code is given below:
data State a = State {
uniform :: a
}
class Renderable a where
render :: (Uniform b) => Int -> a -> State b
library :: (Uniform a) => a -> IO ()
-- some implementation
draw :: (Renderable a) => a -> IO ()
draw renderable = do
let state = render 0 renderable
_ <- library (uniform state)
In the above snippet, the render function tries to enforce that the uniform property in State adheres to a class constraint Uniform. When I run the code, I am getting an error that
Could not deduce (Uniform a5) arising from a use of ‘draw’
from the context: (Renderable r, Uniform a)
bound by the type signature for:
draw :: forall r a.
(Renderable r, Uniform a) =>
Int -> Renderable r -> IO ()
Thinking of it, I am sort of able to understand that since the type of draw uses only Renderable and Renderable does not have a parameter of type Uniform in its type signature, the compiler is not able to verify the flow completely. But I am wondering, why cant the compiler, while testing the type signature of draw ignore the issue and basically depend on the fact that it will know if a type implementing Renderable will definitely have to provide a value for uniform as a part of State and it can verify the type correctness in the implementation site rather than usage.
PS: This is an extracted snippet from OpenGL code and Uniform, Library are opengl terminologies.
Here is a technique for you. I've written about this many years ago (in a slightly different context, but the idea is the same) and I still stand by it.
First, the framing. If we write out the signature of render explicitly, we have:
render :: forall b. Uniform b => Int -> a -> State b
That is, the caller of render chooses the type b. It seems to me that your intention is more like this pseudo-Haskell*:
render :: exists b. (Uniform b) & Int -> a -> State b
In which the callee gets to choose the type. That is, different implementations of render may choose different types b to return, so long as they are uniform.
This might be a fine way to phrase it, except that Haskell does not support existential quantification directly. You can make a wrapper data type to simulate it
data SomeUniform where
SomeUniform :: Uniform a => a -> SomeUniform
making your signature
render :: Int -> a -> SomeUniform
which I think has the properties you are looking for. However the SomeUniform type and the Uniform typeclass are very likely superfluous. You said in the comments that the Uniform typeclass looks like this:
class Uniform a where
library :: a -> IO ()
Let's consider this question: let's say we have a SomeUniform, that is, we have a value of some type a about which we know nothing except that it is an instance of the Uniform typeclass. What can we possibly do with x? There is only one way to get any information out of x, and that is to call library on it. So in essence the only thing the SomeUniform type is doing is carrying around a library method to be called later. This whole existential/typeclass is kind of pointless, we would be better served collapsing it down to a simple data type:
data Uniform = Uniform { library :: IO () }
and your render method becomes:
render :: Int -> a -> Uniform
It's so beautifully unfancy, isn't it? If there were more methods in Uniform typeclass, they would become additional fields of this data type (whose types may be functions, which can take some getting used to). Where you had types and instances of the typeclass, e.g.
data Thingy = Thingy String
-- note the constructor type Thingy :: String -> Thingy
instance Uniform String where
library (Thingy s) = putStrLn $ "thingy " ++ s
you can now also be rid of the data type and just use a function in place of the constructor
thingy :: String -> Uniform
thingy s = Uniform { library = putStrLn $ "thingy " ++ s }
(If you can't get rid of the data type for other reasons, you can provide a conversion function instead uniformThingy :: Thingy -> Uniform)
The principle here is, you may replace an existential type with the collection of its observations, and it's usually pretty nice if you do.
* My pseudo-Haskell & is dual to =>, playing essentially the same role but for existentially quantified dictionaries. c => t means that once the caller provides the dictionary c, the type t is returned, whereas c & t means that the callee provides both the dictionary c and the type t.
It appears that you're expecting to be able to define render to return a different distinct type for each implementation of Renderable, as long as that type is Uniform:
instance Renderable Foo where
render _ _ = State True
instance Renderable Bar where
render _ _ = State "mothman"
instance Renderable Baz where
render _ _ = State 19
So if render is called with a Foo, it will return a State Bool, but if it's called with a Bar it will return a State String (assuming both Bool and String are Uniform). This is not how it works, and you'll get a type mismatch error if you try instantiating like this.
render :: (Uniform b) => Int -> a -> State b means that a Uniform b => State b is returned. If this is what your type signature is, your implementation must be no more or less specific; your implementation must be able to return a value of ANY type Uniform b => State b. If it is not able to do so, any code that requests a return value of a specific type won't get the right type, and things will break in ways that the type system SHOULD be able to prevent.
Let's look at a different example:
class Collection t where
size :: Num i => t a -> i
Assume someone wants to run this size function, and get the result as a Double. They can do that, because any implementation of size must be able to return any type of class Num, so the caller can always specify which type they want. If you were allowed to write an implementation that always returned an Integer, this would no longer be possible.
I think to do what you're trying to do, you'd need something like FunctionalDependencies. With this, your class can be something like:
class Uniform b => Renderable a b | a -> b where
render :: Int -> a -> State b
The "| a -> b" tells the type checker that the type b should be decided based on the type a provided by the caller. This disallows the caller from choosing their own b, which means the implementation should force a more specific type. Note that now you need to specify both a and b in your instances, so:
instance Renderable Foo Bool where ...
instance Renderable Bar String where ...
I'm certain there are other valid approaches to this problem, as well.

Heterogeneous Data.Map in Haskell

Is it possible to do heterogeneous Data.Map in Haskell with GADT instead of Dynamic? I tried to model heterogeneous collection as laid out in this answer:
{-# LANGUAGE GADTs #-}
class Contract a where
toString :: a -> String
data Encapsulated where
Encapsulate :: Contract a => a -> Encapsulated
getTypedObject :: Encapsulated -> a
getTypedObject (Encapsulate x) = x
The idea being that Encapsulated could be used to store different objects of TypeClass a, and then extracted at run-time for specific type.
I get error about type of x not matching Contract a. Perhaps I need to specify some kind of class constraints to tell GHC that type of x in Encapsulate x is same as a in Contract a?
T.hs:10:34:
Couldn't match expected type ‘a’ with actual type ‘a1’
‘a1’ is a rigid type variable bound by
a pattern with constructor
Encapsulate :: forall a. Contract a => a -> Encapsulated,
in an equation for ‘getTypedObject’
at T.hs:10:17
‘a’ is a rigid type variable bound by
the type signature for getTypedObject :: Encapsulated -> a
at T.hs:9:19
Relevant bindings include
x :: a1 (bound at T.hs:10:29)
getTypedObject :: Encapsulated -> a (bound at T.hs:10:1)
In the expression: x
In an equation for ‘getTypedObject’:
getTypedObject (Encapsulate x) = x
I am trying this approach because I have JSON objects of different types, and depending on the type that is decoded at run-time over the wire, we want to retrieve appropriate type-specific builder from Map (loaded at run-time IO in main from configuration files, and passed to the function) and pass it decoded JSON data of the same type.
Dynamic library would work here. However, I am interested in finding out if there are other possible approaches such as GADTs or datafamilies.
your problem is that you push the a out again (which will not work) - what you can do is using the contract internally like this:
useEncapsulateContract :: Encapsulated -> String
useEncapsulateContract (Encapsulate x) = toString x
basically the compiler is telling you everything you need to know: inside you have a forall a. Contract a (so basically a constraint a to be a Contract)
On getTypedObject :: Encapsulated -> a you don't have this constraint - you are telling the compiler: "look this works for every a I'll demand"
To get it there you would have to parametrize Encapsulated to Encapsulated a which you obviously don't want.
The second version (the internal I gave) works because you have the constraint on the data-constructor and so you can use it there
to extent this a little:
this
getTypedObject :: Contract a => Encapsulated -> a
getTypedObject (Encapsulate x) = x
wouldn't work either as now the you would have Contract a but still it could be two different types which just share this class.
And to give hints to the compiler that both should be the same you would have to parametrize Encapsulate again ....
right now by doing this:
Encapsulate :: Contract a => a -> Encapsulated
you erase that information
#Carsten answer is obviously right, but my two cents that helped me understand that before.
When you write:
getTypedObject :: Encapsulated -> a
What you are "saying" is:
getTypedObject is a function that can take a value of the Encapsulated type and its result can be used whenever any type whatsoever is needed.
You can't obviously satisfy that, and the compiler won't allow you to try. You can only use the knowledge about the value inside of Encapsulated to bring out something meaningful based on the Contract. In other words, if the Contract wasn't there, there'd be no way for you to do anything meaningful with that value.
The concept here can be succintly described as type erasure and is also present in other languages, C++ being one I know of. Hence the value is in erasing every information about the type except the things you want to preserve via the contract they satisfy. The downside is that getting the original types back requires runtime inspection.
As a bonus, here's how a dynamic approach might work:
{-# LANGUAGE GADTs #-}
import Unsafe.Coerce
data Encapsulated where
Encapsulate :: Show a => a -> Encapsulated
getTypedObject :: Encapsulated -> a
getTypedObject (Encapsulate x) = unsafeCoerce x
printString :: String -> IO ()
printString = print
x = Encapsulate "xyz"
y = getTypedObject x
main = printString y
But it's very easy to see how that could break, right? :)

Deserializing an existential data type

I need to write a Serialize instance for the following data type:
data AnyNode = forall n . (Typeable n, Serialize n) => AnyNode n
Serializing this is no problem, but I can't implement deserialization, since the compiler has no way to resolve the specific instance of Serialize n, since the n is isolated from the outer scope.
There's been a related discussion in 2006. I am now wondering whether any sort of solution or a workaround has arrived today.
You just tag the type when you serialize, and use a dictionary to untag the type when you deserialize. Here's some pseudocode omitting error checking etc:
serialAnyNode (AnyNode x) = serialize (typeOf n, serialize x)
deserialAnyNode s = case deserialize s of
(typ,bs) -> case typ of
"String" -> AnyNode (deserialize bs :: String)
"Int" -> AnyNode (deserialize bs :: Int)
....
Note that you can only deserialize a closed universe of types with your function. With some extra work, you can also deserialize derived types like tuples, maybes and eithers.
But if I were to declare an entirely new type "Gotcha" deriving Typeable and Serialize, deserialAnyNode of course couldn't deal with it without extension.
You need to have some kind of centralised "registry" of deserialization functions so you can dispatch on the actual type (extracted from the Typeable information). If all types you want to deserialize are in the same module this is pretty easy to set up. If they are in multiple modules you need to have one module that has the mapping.
If your collection of types is more dynamic and not easily available at compile time, you can perhaps use the dynamic linking to gain access to the deserializers. For each type that you want to deserialize you export a C callable function with a name derived from the Typeable information (you could use TH to generate these). Then at runtime, when you want to deserialize a type, generate the same name and the use the dynamic linker to get hold of the address of the function and then an FFI wrapper to get a Haskell callable function. This is a rather involved process, but it can be wrapped up in a library. No, sorry, I don't have such a library.
It's hard to tell what you're asking here, exactly. You can certainly pick a particular type T, deserialize a ByteString to it, and store it in an AnyNode. That doesn't do the user of an AnyNode much good, though -- you still picked T, after all. If it wasn't for the Typeable constraint, the user wouldn't even be able to tell what the type is (so let's get rid of the Typeable constraint because it makes things messier). Maybe what you want is a universal instead of an existential.
Let's split Serialize up into two classes -- call them Read and Show -- and simplify them a bit (so e.g. read can't fail).
So we have
class Show a where show :: a -> String
class Read a where read :: String -> a
We can make an existential container for a Show-able value:
data ShowEx where
ShowEx :: forall a. Show a => a -> ShowEx
-- non-GADT: data ShowEx = forall a. Show a => ShowEx a
But of course ShowEx is isomorphic to String, so there isn't a whole lot point to this. But note that an existential for Read is has even less point:
data ReadEx where
ReadEx :: forall a. Read a => a -> ReadEx
-- non-GADT: data ReadEx = forall a. Read a => ReadEx a
When I give you a ReadEx -- i.e. ∃a. Read a *> a -- it means that you have a value of some type, and you don't know what the type is, but you can a String into another value of the same type. But you can't do anything with it! read only produces as, but that doesn't do you any good when you don't know what a is.
What you might want with Read would be a type that lets the caller choose -- i.e., a universal. Something like
newtype ReadUn where
ReadUn :: (forall a. Read a => a) -> ReadUn
-- non-GADT: newtype ReadUn = ReadUn (forall a. Read a => a)
(Like ReadEx, you could make ShowUn -- i.e. ∀a. Show a => a -- and it would be just as useless.)
Note that ShowEx is essentially the argument to show -- i.e. show :: (∃a. Show a *> a) -> String -- and ReadUn is essentially the return value of read -- i.e. read :: String -> (∀a. Read a => a).
So what are you asking for, an existential or a universal? You can certainly make something like ∀a. (Show a, Read a) => a or ∃a. (Show a, Read a) *> a, but neither does you much good here. The real issue is the quantifier.
(I asked a question a while ago where I talked about some of this in another context.)

Polymorphic Return Types Depending on Context

I'm playing around with implementing a Redis client-library in Haskell and it is my goal to encode, as much as possible, the semantics of the Redis commands in Haskell's type system. Redis, for those who don't know, is a datastore, accessed over the network. I will use it to exemplify my problem, but Redis is not the focus of this question.
An Example Function
Consider the function
get :: (RedisValue a) => Key -> Redis a
get k = decodeValue <$> sendCommand ["GET", key]
It sends a command to the datastore and returns a value stored under the given Key (for this example, you can consider type Key = String). As for the return-type:
Redis is an instance of Monad and MonadIO. It encapsulates information about the network connection. sendCommand sends the request and returns the datastore's reply.
a is polymorphic, for example either Strings or ByteStrings can be returned, depending on the context.
The following code should clarify the text above.
data Redis a = ...
instance MonadIO Redis where ...
instance Monad Redis where ...
sendCommand :: [String] -> Redis String
class RedisValue a where
decodeValue :: String -> a
-- example instances
instance RedisValue String where ...
instance RedisValue ByteString where ...
Different Context, Different Types
Redis supports a simple form of transactions. In a transaction most commands can be sent the same as outside of a transaction. However their execution is delayed until the user sends the commit command (which is called exec in Redis). Inside the transaction, the datastore only returns an acknowledgment that the command is stored for later execution. Upon commit (exec) all results of all stored commands are returned.
This means that the get-function from above looks a bit different in the context of a transaction:
get :: (RedisStatus a) => Key -> RedisTransaction a
get k = decodeStatus <$> sendCommand ["GET", key]
Note that:
The monadic type is now RedisTransaction to indicate the transaction context.
The a return type is now any instance of RedisStatus. There is an overlap between instances of RedisValue and RedisStatus. For example String is in both classes. A specialized Status data type might be only in the RedisStatus class.
The Actual Question
My question is, how can I write a function get that works in both contexts, with context-appropriate return type classes. What I need is
a way to give get a return type "either Redis or RedisTransaction",
The type a to be an instance of RedisValue in the Redis context and an instance of RedisStatus in the RedisTransaction context.
A function decode that automagically does the right thing, depending on the context. I assume this must come from a (multi-param) type class.
If you know how I can do this or have a pointer to some example code or even an article, you will have my thanks!
First, I think that it would be better to have two different get commands. That said, here's an approach.
class RedisGet m a where
get :: Key -> m a
instance (RedisValue a) => RedisGet Redis a where...
instance (RedisStatus a) => RedisGet RedisTransaction a where...
You need
MPTCs, but no FunDeps or Type Families. Every use of get requires that enough information be available to determine both the m and a uniquely.
I agree that multiparameter type classes are a good fit here. Here's an approach:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
newtype Redis a = Redis (IO a) deriving Monad
newtype RedisTransaction a = RedisTransaction (IO a) deriving Monad
newtype Key = Key {unKey :: String}
newtype Value = Value {unValue :: String}
newtype Status = Status {unStatus :: String}
class Monad m => RedisMonad m a | m -> a where
sendCommand :: [String] -> m a
instance RedisMonad Redis Value where
sendCommand = undefined -- TODO: provide implementation
instance RedisMonad RedisTransaction Status where
sendCommand = undefined -- TODO: provide implementation
class Decodable a b where
decode :: a -> b
instance Decodable Status String where
decode = unStatus
instance Decodable Value String where
decode = unValue
get :: (RedisMonad m a, Decodable a b) => Key -> m b
get k = do
response <- sendCommand ["GET", unKey k]
return (decode response)
Note the use of the type isomorphisms for Value and Status: it makes things slightly stronger typed as the Strings you are having produced by your implementations of sendCommand obviously are not just arbitrary sequences of characters but instead adhere to some fixed formats for return values and statuses.
Keep in mind that there's nothing special about a type depending on context--that happens all the time with type inference. The type of [] is [a], but when you use it in something like True : [] the type will be specialized to [Bool] in context.
What changes matters is if you want the implementation of a function, or the definition of a value, to depend on its type. If that type is then inferred from context in the normal way, you end up with a function that does something "different" depending on context. Type-dependent implementation is the main purpose of using type classes.
Now, to answer your specific questions:
a way to give get a return type "either Redis or RedisTransaction",
This requires only a variable in the type signature of get, e.g. get :: Key -> f a. The f will be filled in as either Redis or RedisTransaction depending on context.
The type a to be an instance of RedisValue in the Redis context and an instance of RedisStatus in the RedisTransaction context.
Since both a and the context type will be inferred from use, what you're really after here is restricting the possible types, which amounts to expecting a type-checking error if they don't match up. This is another purpose of type classes, and could be implemented with an appropriate class constraint on the context type variable:
get :: (ContextValue (f a)) => Key -> f a
class ContextValue a
instance (RedisValue a) => ContextValue (Redis a)
instance (RedisStatus a) => ContextValue (RedisTransaction a)
Or something like that. But that alone isn't enough for your purposes, because...
A function decode that automagically does the right thing, depending on the context. I assume this must come from a (multi-param) type class.
This implies selecting an implementation for decode based on the type, which means making it part of a type class such as the above ContextValue. How you handle this depends on what the type for decode needs to be--if the result type needs to be something like f String -> f a where f is the monadic context, then you'll probably need something a bit more elaborate, like in dblhelix's answer. If you only need String -> f a, then you could add it to the above ContextValue class directly.

Resources