Haskell: How to Construct a Heterogeneous Type Any
I would like to create a type that matches anything but never gets used.
Example:
type Any = forall a. a
f :: (x, Any) -> (Any, y) -> (x,y)
f (x,_) (_,y) = (x,y)
This only compiles with {-# LANGUAGE ImpredicativeTypes #-}
, but if I try
f ("hi", 2) (3, (1, 2))
I am getting the error:
<interactive>:19:9:
No instance for (Num a) arising from the literal `2'
Possible fix:
add (Num a) to the context of a type expected by the context: a
In the expression: 2
In the first argument of `f', namely `("hi", 2)'
In the expression: f ("hi", 2) (3, (1, 2))
<interactive>:19:13:
No instance for (Num a) arising from the literal `3'
Possible fix:
add (Num a) to the context of a type expected by the context: a
In the expression: 3
In the second argument of `f', namely `(3, (1, 2))'
In the expression: f ("hi", 2) (3, (1, 2))
Which would be nice if I just wanted x and y to be Num, but what I plan to do with this should be much more flexible than that. I understand that it forall a. a
fits all types, but can only convey a tone that can never be calculated and lowered. But I have no desire to ever look at the Any type.
source to share
I think there is a fundamental misunderstanding of type Any
. Let me explain with a few examples.
Any manufacturer function
f :: ... -> Any
can be used to create a value that is of any type: it returns a string that is also an integer, and a pair and an elephant at the same time. Specifically, it returns the bottom (or it doesn't return at all, if you prefer).
Any consumer function
f :: Any -> ...
expects to be loaded with any type of value: the caller must supply a string, which is also an integer, and a pair and an elephant at the same time. Specifically, the caller must pass from below.
You are trying to pass 2
in which has no type - it only has any numeric type. Hence a type error.
If you want to write a function that accepts anything, you should write
type Any = exists a. a -- INVALID Haskell
f :: Any -> ...
but alas, Haskell does not allow this kind of existential type. If you want to use this type, you need to insert it:
data Any = forall a . Any a
f :: Any -> ...
caller = f (Any 'd')
Alternatively, you can take it exists
to the top level. Since it is in the negative position, it becomesforall
f :: (exists a. a) -> ...
-- becomes
f :: forall a. (a -> ...)
source to share
The real question seems to be raised from the comments: How do I type a list written with literal syntax ["a", False]
?
The answer (thankfully!) Is "you can't".
You can create an existential type and wrap each element in an existential one. If you want to do it, you can do it like this:
{-# LANGUAGE GADTs #-}
data Box where
Box :: a -> Box
Then the list [Box "a", Box False]
will print well in type [Box]
. However, if you want to apply a function to every element, you can also skip all types of shenanigans and do something like this:
toss :: a -> ()
toss _ = ()
Then [toss "a", toss False]
has a very clear type [()]
.
source to share
It can't work because yours Any
really is All
. It can only be built from an expression having every type (sort of undefined
).
You will need to use {-# LANGUAGE ExistentialQuantification #-} to build a real
Any`:
data Any = forall a . Any a
It has to be a datatype, so you'll have to create values using a constructor Any
, but now you can do something like this:
f :: [(x, Any)] -> [(Any, y)] -> [(x,y)]
f ((x, _) : xs) ((_, y) : ys) = (x,y) : f xs ys
f _ _ = []
> f [("hi", Any 'a'),("you", Any 3)] [(Any "a", 2),(Any Nothing, 4)]
[("hi",2),("you",4)]
source to share