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.

+3


source to share


3 answers


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 -> ...)

      

+6


source


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 [()]

.

+5


source


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)]

      

+3


source







All Articles