4

data Foo a is defined like:

data Foo a where
  Foo :: (Typeable a, Show a) => a -> Foo a
  -- perhaps more constructors

instance Show a => Show (Foo a) where
  show (Foo a) = show a

with some instances:

fiveFoo :: Foo Int
fiveFoo = Foo 5

falseFoo :: Foo Bool
falseFoo = Foo False

How can I define any function from b -> Foo a, for example:

getFoo :: (Show a, Typeable a) => String -> Foo a
getFoo "five" = fiveFoo
getFoo "false" = falseFoo

Here getFoo does not type check with Couldn't match type ‘a’ with ‘Bool’.

The only thing that I am interested in here is for a to be of class Showso I can use getFoo like:

main = getLine >>= (print . getFoo)
Pi Delport
  • 10,356
  • 3
  • 36
  • 50
homam
  • 1,945
  • 1
  • 19
  • 26

3 Answers3

5

You can use existential types to make a data type hide and "carry" a type class like Show around.

Note that using existential types like this is considered to be an anti-pattern in Haskell, and you probably want to consider carefully whether you really want to do this: being more explicit about your types is usually simpler, better, and less prone to bugs.

However, that being said, if you really want to do this, here is how you would use existential types with your example:

{-# LANGUAGE ExistentialQuantification #-}

-- This Foo can only be constructed with instances of Show as its argument.
data Foo = forall a. Show a => Foo a

-- Note that there is no "Show a => ..." context here:
-- Foo itself already carries that constraint around with it.
instance Show Foo where
  show (Foo a) = show a


getFoo :: String -> Foo
getFoo "five" = Foo 5
getFoo "false" = Foo False

main = print . getFoo =<< getLine

Demonstration:

ghci> main
five
5
ghci> main
false
False
Pi Delport
  • 10,356
  • 3
  • 36
  • 50
4

Perhaps you want to omit the type parameter from Foo.

data Foo where
  Foo :: (Typeable a, Show a) => a -> Foo

instance Show Foo where
  show (Foo a) = show a

fiveFoo :: Foo
fiveFoo = Foo (5 :: Int) -- (Foo 5) doesn't work because of ambiguity

falseFoo :: Foo
falseFoo = Foo False

getFoo :: String -> Foo
getFoo "five" = fiveFoo
getFoo "false" = falseFoo

print $ getFoo "five" -- prints '5'
print $ getFoo "false" -- prints 'False'
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • 5
    I like this, but I wonder if the OP really needs it -- it's easy to fall in the [existential antipattern](https://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/). Only the OP knows, though. – chi Aug 25 '16 at 11:53
  • @chi yes this is existential thingy but I don't think it's so much an antipattern. I know I'm an a minority here. – n. m. could be an AI Aug 25 '16 at 12:22
  • Did we made `Foo` a union type here? Is this equivalent to `data Foo = IFoo Int | BFoo Bool`? – homam Aug 25 '16 at 12:28
  • @n.m. Well, I also think Luke Palmer is a bit too extreme in its blog post. Still, I think using e.g. a list of Show-ables instead of a list of strings is a pointless overcomplication. In my view the type `Foo` above is valuable, but only because of the `Typeable` constraint, which allows one to `cast` later on and recover the type. Otherwise, `Foo` is isomorphic to a string (or, at most, to a string-builder function as in `shows`) so using existentials looks a bit overkill. – chi Aug 25 '16 at 13:11
  • @homam No it's not equivalent. `Foo` can also contain strings, pairs of ints, lists of lists of ints, etc. It can wrap anything which is showable (and typeable). – chi Aug 25 '16 at 13:13
2
getFoo :: (Show a, Typeable a) => String -> Foo a
getFoo "five" = fiveFoo
getFoo "false" = falseFoo

If fiveFoo :: Foo Int and falseFoo :: Foo Bool, you're essentially asking for getFoo to return a different type depending on what value you feed it at run-time. You can't do that. In Haskell, all types must be known at compile-time.

If all you want to be able to do is convert the thing to a string, why not just store it as a string in the first place? I'm guessing the answer is that this is actually a simplification of the real problem you're trying to solve...(?)

MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220
  • 1
    Well you're right. I tried to boil down my problem to its simplest. I want to create a series of `Nodes a b` (like a Category) so that each Node processes an input from user and continue with either another `Node b a'` or with the final result `b`. – homam Aug 25 '16 at 12:19
  • 1
    This is closer to my real problem: http://stackoverflow.com/questions/39147517/functions-of-gadts – homam Aug 25 '16 at 14:10