Polymorphic Types: representing multiple types using the same type synonym - haskell

My objective is giving two data types the same synonym.
I've minimized my question to the following question:
{-# LANGUAGE KindSignatures, Rank2Types #-}
class Things (h :: * -> *) where
newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a
instance Things Thing1 where
instance Things Thing2 where
type OneOfTwo a = forall h. Things h => h a
foo :: OneOfTwo String -> String
foo (Thing1 name) = name
foo (Thing2 name) = name
My goal is to be able to make the type synonym oneOfTwo stand for either Thing1 and Thing2 (or even more if I want to). But when I do so, any pattern matching aside from the first one in foo will be seen as redundant:
Pattern match is redundant
|
15 | foo (Thing2 name) = name
|
I'm aware that I can re-write it as the following:
newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a
data OneOfTwo a = One (Thing1 a)
| Two (Thing2 a)
foo :: OneOfTwo String -> String
foo (One (Thing1 name)) = name
foo (Two (Thing2 name)) = name
But I want to avoid creating a new data type OneOfTwo.
Is there a workaround to make this happen without creating a new boxing data type?

You've tried to build an existential type
type OneOfTwoE a = ∃ h. Things h => h a
meaning “whoever uses a value v of type OneOfTwo a can be sure that there is some concrete type constructor h, which is an instance of Things, such that v has type h a”. But that's not what the type you wrote means; that is a universal type
type OneOfTwoA a = ∀ h. Things h => h a
meaning that for all type constructors h that the user of v might think of, v has type h a. I.e. the single value v has in general multiple different types!
That means you can actually write
fooA :: OneOfTwoA String -> String
fooA v = (case v of {Thing1 name -> name})
++ (case v of {Thing2 name -> name})
and both of the case matches will succeed, despite matching on different constructors! This works because in each of the case expressions, v is specialised to a different h instantiation, which is ok because the type is universal.
Haskell doesn't really have existential types. It does, as you've noted, have discriminated sum types. It also can simulated existential types with universally quantified data constructors, preferrably expressed with GADT syntax:
data OneOfTwoG a where
OneOfTwo :: ∀ h a . Things h => h a -> OneOfTwoG a
which makes it conceptually possible to have something like (this code doesn't work)
fooG :: OneOfTwoG String -> String
fooG (OneOfTwo (Thing1 name)) = name
fooG (OneOfTwo (Thing2 name)) = name
To actually get that functionality, you need to put the case distinction into the class instances, like
class Things (h :: * -> *) where
gname :: h a -> a
newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a
instance Things Thing1 where gname (Thing1 n) = n
instance Things Thing2 where gname (Thing2 n) = n
fooG :: OneOfTwoG String -> String
fooG (OneOfTwo ϑ) = gname ϑ

At the risk of pointing out the obvious, the whole purpose of type classes is to allow you to define polymorphic functions that can be applied to multiple types, so what you're supposed to do in this situation is write:
class Things h where
foo :: h String -> String
newtype Thing1 a = Thing1 a
newtype Thing2 a = Thing2 a
instance Things Thing1 where
foo (Thing1 name) = name
instance Things Thing2 where
foo (Thing2 name) = name
That is, when you want to represent multiple types with a single name, the tool you want to use is a type class, not a type synonym.

Related

Deriving Eq and Show for an ADT that contains fields that can't have Eq or Show

I'd like to be able to derive Eq and Show for an ADT that contains multiple fields. One of them is a function field. When doing Show, I'd like it to display something bogus, like e.g. "<function>"; when doing Eq, I'd like it to ignore that field. How can I best do this without hand-writing a full instance for Show and Eq?
I don't want to wrap the function field inside a newtype and write my own Eq and Show for that - it would be too bothersome to use like that.
One way you can get proper Eq and Show instances is to, instead of hard-coding that function field, make it a type parameter and provide a function that just “erases” that field. I.e., if you have
data Foo = Foo
{ fooI :: Int
, fooF :: Int -> Int }
you change it to
data Foo' f = Foo
{ _fooI :: Int
, _fooF :: f }
deriving (Eq, Show)
type Foo = Foo' (Int -> Int)
eraseFn :: Foo -> Foo' ()
eraseFn foo = foo{ fooF = () }
Then, Foo will still not be Eq- or Showable (which after all it shouldn't be), but to make a Foo value showable you can just wrap it in eraseFn.
Typically what I do in this circumstance is exactly what you say you don’t want to do, namely, wrap the function in a newtype and provide a Show for that:
data T1
{ f :: X -> Y
, xs :: [String]
, ys :: [Bool]
}
data T2
{ f :: OpaqueFunction X Y
, xs :: [String]
, ys :: [Bool]
}
deriving (Show)
newtype OpaqueFunction a b = OpaqueFunction (a -> b)
instance Show (OpaqueFunction a b) where
show = const "<function>"
If you don’t want to do that, you can instead make the function a type parameter, and substitute it out when Showing the type:
data T3' a
{ f :: a
, xs :: [String]
, ys :: [Bool]
}
deriving (Functor, Show)
newtype T3 = T3 (T3' (X -> Y))
data Opaque = Opaque
instance Show Opaque where
show = const "..."
instance Show T3 where
show (T3 t) = show (Opaque <$ t)
Or I’ll refactor my data type to derive Show only for the parts I want to be Showable by default, and override the other parts:
data T4 = T4
{ f :: X -> Y
, xys :: T4' -- Move the other fields into another type.
}
instance Show T4 where
show (T4 f xys) = "T4 <function> " <> show xys
data T4' = T4'
{ xs :: [String]
, ys :: [Bool]
}
deriving (Show) -- Derive ‘Show’ for the showable fields.
Or if my type is small, I’ll use a newtype instead of data, and derive Show via something like OpaqueFunction:
{-# LANGUAGE DerivingVia #-}
newtype T5 = T5 (X -> Y, [String], [Bool])
deriving (Show) via (OpaqueFunction X Y, [String], [Bool])
You can use the iso-deriving package to do this for data types using lenses if you care about keeping the field names / record accessors.
As for Eq (or Ord), it’s not a good idea to have an instance that equates values that can be observably distinguished in some way, since some code will treat them as identical and other code will not, and now you’re forced to care about stability: in some circumstance where I have a == b, should I pick a or b? This is why substitutability is a law for Eq: forall x y f. (x == y) ==> (f x == f y) if f is a “public” function that upholds the invariants of the type of x and y (although floating-point also violates this). A better choice is something like T4 above, having equality only for the parts of a type that can satisfy the laws, or explicitly using comparison modulo some function at use sites, e.g., comparing someField.
The module Text.Show.Functions in base provides a show instance for functions that displays <function>. To use it, just:
import Text.Show.Functions
It just defines an instance something like:
instance Show (a -> b) where
show _ = "<function>"
Similarly, you can define your own Eq instance:
import Text.Show.Functions
instance Eq (a -> b) where
-- all functions are equal...
-- ...though some are more equal than others
_ == _ = True
data Foo = Foo Int Double (Int -> Int) deriving (Show, Eq)
main = do
print $ Foo 1 2.0 (+1)
print $ Foo 1 2.0 (+1) == Foo 1 2.0 (+2) -- is True
This will be an orphan instance, so you'll get a warning with -Wall.
Obviously, these instances will apply to all functions. You can write instances for a more specialized function type (e.g., only for Int -> String, if that's the type of the function field in your data type), but there is no way to simultaneously (1) use the built-in Eq and Show deriving mechanisms to derive instances for your datatype, (2) not introduce a newtype wrapper for the function field (or some other type polymorphism as mentioned in the other answers), and (3) only have the function instances apply to the function field of your data type and not other function values of the same type.
If you really want to limit applicability of the custom function instances without a newtype wrapper, you'd probably need to build your own generics-based solution, which wouldn't make much sense unless you wanted to do this for a lot of data types. If you go this route, then the Generics.Deriving.Show and Generics.Deriving.Eq modules in generic-deriving provide templates for these instances which could be modified to treat functions specially, allowing you to derive per-datatype instances using some stub instances something like:
instance Show Foo where showsPrec = myGenericShowsPrec
instance Eq Foo where (==) = myGenericEquality
I proposed an idea for adding annotations to fields via fields, that allows operating on behaviour of individual fields.
data A = A
{ a :: Int
, b :: Int
, c :: Int -> Int via Ignore (Int->Int)
}
deriving
stock GHC.Generic
deriving (Eq, Show)
via Generically A -- assuming Eq (Generically A)
-- Show (Generically A)
But this is already possible with the "microsurgery" library, but you might have to write some boilerplate to get it going. Another solution is to write separate behaviour in "sums-of-products style"
data A = A Int Int (Int->Int)
deriving
stock GHC.Generic
deriving
anyclass SOP.Generic
deriving (Eq, Show)
via A <-𝈖-> '[ '[ Int, Int, Ignore (Int->Int) ] ]

What's the difference between the "data" and "type" keywords?

The data and type keywords always confuse me.
I want to know what is the difference between data and type and how to use them.
type declares a type synonym. A type synonym is a new name for an existing type. For example, this is how String is defined in the standard library:
type String = [Char]
String is another name for a list of Chars. GHC will replace all usages of String in your program with [Char] at compile-time.
To be clear, a String literally is a list of Chars. It's just an alias. You can use all the standard list functions on String values:
-- length :: [a] -> Int
ghci> length "haskell"
7
-- reverse :: [a] -> [a]
ghci> reverse "functional"
"lanoitcnuf"
data declares a new data type, which, unlike a type synonym, is different from any other type. Data types have a number of constructors defining the possible cases of your type. For example, this is how Bool is defined in the standard library:
data Bool = False | True
A Bool value can be either True or False. Data types support pattern matching, allowing you to perform a runtime case-analysis on a value of a data type.
yesno :: Bool -> String
yesno True = "yes"
yesno False = "no"
data types can have multiple constructors (as with Bool), can be parameterised by other types, can contain other types inside them, and can recursively refer to themselves. Here's a model of exceptions which demonstrates this; an Error a contains an error message of type a, and possibly the error which caused it.
data Error a = Error { value :: a, cause :: Maybe (Error a) }
type ErrorWithMessage = Error String
myError1, myError2 :: ErrorWithMessage
myError1 = Error "woops" Nothing
myError2 = Error "myError1 was thrown" (Just myError1)
It's important to realise that data declares a new type which is apart from any other type in the system. If String had been declared as a data type containing a list of Chars (rather than a type synonym), you wouldn't be able to use any list functions on it.
data String = MkString [Char]
myString = MkString ['h', 'e', 'l', 'l', 'o']
myReversedString = reverse myString -- type error
There's one more variety of type declaration: newtype. This works rather like a data declaration - it introduces a new data type separate from any other type, and can be pattern matched - except you are restricted to a single constructor with a single field. In other words, a newtype is a data type which wraps up an existing type.
The important difference is the cost of a newtype: the compiler promises that a newtype is represented in the same way as the type it wraps. There's no runtime cost to packing or unpacking a newtype. This makes newtypes useful for making administrative (rather than structural) distinctions between values.
newtypes interact well with type classes. For example, consider Monoid, the class of types with a way to combine elements (mappend) and a special 'empty' element (mempty). Int can be made into a Monoid in many ways, including addition with 0 and multiplication with 1. How can we choose which one to use for a possible Monoid instance of Int? It's better not to express a preference, and use newtypes to enable either usage with no runtime cost. Paraphrasing the standard library:
-- introduce a type Sum with a constructor Sum which wraps an Int, and an extractor getSum which gives you back the Int
newtype Sum = Sum { getSum :: Int }
instance Monoid Sum where
(Sum x) `mappend` (Sum y) = Sum (x + y)
mempty = Sum 0
newtype Product = Product { getProduct :: Int }
instance Monoid Product where
(Product x) `mappend` (Product y) = Product (x * y)
mempty = Product 1
With data you create new datatype and declare a constructor for it:
data NewData = NewDataConstructor
With type you define just an alias:
type MyChar = Char
In the type case you can pass value of MyChar type to function expecting a Char and vice versa, but you can't do this for data MyChar = MyChar Char.
type works just like let: it allows you to give a re-usable name to something, but that something will always work just as if you had inlined the definition. So
type ℝ = Double
f :: ℝ -> ℝ -> ℝ
f x y = let x2 = x^2
in x2 + y
behaves exactly the same way as
f' :: Double -> Double -> Double
f' x y = x^2 + y
as in: you can anywhere in your code replace f with f' and vice versa; nothing would change.
OTOH, both data and newtype create an opaque abstraction. They are more like a class constructor in OO: even though some value is implemented simply in terms of a single number, it doesn't necessarily behave like such a number. For instance,
newtype Logscaledℝ = LogScaledℝ { getLogscaled :: Double }
instance Num LogScaledℝ where
LogScaledℝ a + LogScaledℝ b = LogScaledℝ $ a*b
LogScaledℝ a - LogScaledℝ b = LogScaledℝ $ a/b
LogScaledℝ a * LogScaledℝ b = LogScaledℝ $ a**b
Here, although Logscaledℝ is data-wise still just a Double number, it clearly behaves different from Double.

Check if it is a specific type - haskell

I thought it would be really easy to find the answer online but I had no luck with that. Which means that my question should't be a question but I am sure more people new to Haskell might come up with the same question.
So how do I check if a value is of a certain type?
I have the following data type defined and I wanna check whether the input on a function is of a specific type.
data MyType a = MyInt Int | MyOther a (MyType a)
First, your data declaration will not work. Let's assume you're using this type:
data MyType a = MyInt Int | MyOther a (MyType a)
then you can have functions that take a MyType a, some specific MyType (e.g. MyType Int) or a constrained MyType (e.g. Num a => MyType a).
If you want to know whether you have a MyInt or a MyOther, you can simply use pattern matching:
whichAmI :: MyType a -> String
whichAmI (MyInt i) = "I'm an Int with value " ++ show i
whichAmI (MyOther _ _) = "I'm something else"
When you want to know if the type in the parameter a is a Num, or what type it is, you will run into a fundamental Haskell limitation. Haskell is statically typed so there is no such dynamic checking of what the a in MyType a is.
The solution is to limit your function if you need a certain type of a. For example we can have:
mySum :: Num a => MyType a -> a
mySum (MyInt i) = fromIntegral i
mySum (MyOther n m) = n + mySum m
or we can have a function that only works if a is a Bool:
trueOrGE10 :: MyType Bool -> Bool
trueOrGE10 (MyInt i) = i >= 10
trueOrGE10 (MyOther b _) = b
As with all Haskell code, it will need to be possible to determine at compile-time whether a particular expression you put into one of these functions has the right type.

Functors don’t work with data types that require specific types

This works fine:
data Foo a = Foo a
instance Functor Foo where
fmap f (Foo s) = Foo (f s)
This throws an Error:
data Foo = Foo String
instance Functor Foo where
fmap f (Foo s) = Foo (f s)
Error:
Kind mis-match
The first argument of `Functor' should have kind `* -> *',
but `Foo' has kind `*'
In the instance declaration for `Functor Foo'
What am I missing here? Why can’t I use functors to wrap and unwrap Foo if it holds a specific type?
UPDATE
I guess I can ask this another way:
data Foo = Foo String deriving(Show)
let jack = Foo "Jack"
-- Some functory thingy here
putStrLn $ show $ tail <$> jack
-- Foo "ack"
Why can't I do this? Or is there another construct for this use case?
That's because Foo needs a single type variable to operate on.
The type of fmap is:
fmap :: Functor f => (a -> b) -> f a -> f b
Now try to specialize this for your Foo:
(a -> b) -> Foo -> Foo
Can you see where the problem is ? The types won't just match. So, to make Foo a functor, it has to be something like this:
Foo a
so that when you specialize it for fmap it has the following proper type:
(a -> b) -> Foo a -> Foo b
The type of fmap is generic; you can't constrain it:
fmap :: Functor f => (a -> b) -> f a -> f b
Those as and bs must be completely polymorphic (within the constraints of your Functor instance), or you don't have a Functor. The handwavy way of explaining why this is is because a Functor must obey some theoretical laws to make them play nice with Haskell's other data types:
fmap id = id
fmap (p . q) = (fmap p) . (fmap q)
If you have a data type that is parameterized over multiple types, i.e:
data Bar a b = Bar a b
You can write a Functor instance for Bar a:
instance Functor (Bar a) where
fmap f (Bar a b) = Bar a (f b)
You can also write a Bifunctor instance for Bar:
instance Bifunctor Foo where
first f (Bar a b) = Bar (f a) b
second f (Bar a b) = Bar a (f b)
...which again must follow some laws (listed on the linked page).
Edit:
You could write your own class to handle the type of behavior you're looking for, but it would look like this:
class FooFunctor f where
ffmap :: (String -> String) -> f -> f
But in this case, we'd have to make new entire classes for every single permutation of "inner types" we might have (like String), in order to cover all bases.
You can also write a class (call it Endo) that only permits endomorphisms (functions of type a -> a) on the "inner type" of a data type, like this:
class Endo f where
emap :: (a -> a) -> f a -> f a
Then, if you changed your data type a bit, and instantiated an appropriate instance of Endo, e.g.
data Foo' a = Foo' a
type Foo = Foo' String
instance Endo Foo' where
emap f (Foo a) = Foo (f a)
...if you write functions of type Foo -> Foo, you're guaranteed to preserve the "Stringiness" of the inner type you're mapping if you use emap. A quick search on hayoo reveals that this type of thing is relatively common practice, but doesn't really exist as a standard type class.
Coming from a dynamic language you probably see a Functor as a container of stuff and fmap as a way to transform things inside the container. However in Category theory a Functor can be seen as a way to transform a type into another type, and a way to transform a function on those type to function on the other type.
Imagine you have 2 differents worlds, one which is earth, and a virtual one when every body/things on earth has an avatar. The Functor is not the avatar but the magic wand which transforms everything to its avatar but also every single function of the real world into a function in the avatar world.
For example, with my magic wand I can transform a human to a frog (or a String to a list of Strings) but I can also transform the function "change the human hat" to change the frog hat" (or capitaize a String to capitalize all the String within a list).
fmap is the way you transform a function to another : you can see it as
a function which take 2 arguments - a function and a container - and apply this function to each element of this container
but also as a function which take 1 argmunt - a function - an return a function which takes a container and return a container.
The way you create a type from a type is less obvious In your first example you probably just see Foo String as new type, but you can also see Foo as a super function whic take the type String and return a new type : Foo String. That's what the * -> * kind is. Foo is not a type but a super function creating type from a type.
In your second example, Foo is not a type creator but just a simple type (kind : *), therefore it doesn't make sense to declare it as a functor.
If you really want to define fmap for the plain Foo in your 2nd example is to define a real functor and create a type alias for the plain type
data FooFunctor a = FooFunctor a
instance Functor Foofunctor where
fmap f (FooFunctor a) = FooFunctor (f a)
type Foo = FooFunctor String
A class that does pretty much exactly what you're asking for is MonoFunctor.
type instance Element Foo = String
instance MonoFunctor Foo where
fmap f (Foo s) = Foo (f s)
head "Jack" is not the string "J", but the character 'J'. So your own example shows why this does not work; head <$> jack would have to give Foo 'J', which is not a valid value of type Foo, since Foo can only be applied to String values, not Char values.
"Some other construct" for this use case is to define a "map" function for Foo, exactly as you're trying to define fmap. But that map function is not fmap, since it has to have type (String -> String) -> Foo -> Foo. So there's no need (or possibility) to make Foo an instance of Functor and name your mapping function fmap; the mapping function you want to use is simply not fmap.
Note that this means you cannot map arbitrary functions over your Foo values; only functions which take and return strings (so head is still out). Nor can you pass Foo values to generic functions that accept values in any functor; those functions might try to fmap functions over Foo that do not return strings; they're allowed to do this because they specified that they need functors, and that's exactly what defines a functor.

Can you pattern match constructors on a type class constrained parameter?

See code example below. It won't compile. I had thought that maybe it's because it has to have a single type for the first parameter in the test function. But that doesn't make sense because if I don't pattern match on it so it will compile, I can call it with both MyObj11 5 and MyObj21 5 which are two different types.
So what is it that restricts so you can't pattern match on constructors with a type class constrained parameter? Or is there some mechanism by which you can?
class SomeClass a where toString :: a -> String
instance SomeClass MyType1 where toString v = "MyType1"
instance SomeClass MyType2 where toString v = "MyType2"
data MyType1 = MyObj11 Int | MyObj12 Int Int
data MyType2 = MyObj21 Int | MyObj22 Int Int
test :: SomeClass a => a -> String
test (MyObj11 x) = "11"
test (MyObj12 x y) = "12" -- Error here if remove 3rd line: rigid type bound error
test (MyObj22 x y) = "22" -- Error here about not match MyType1.
what is it that restricts so you can't pattern match on constructors with a type class constrained parameter?
When you pattern match on an explicit constructor, you commit to a specific data type representation. This data type is not shared among all instances of the class, and so it is simply not possible to write a function that works for all instances in this way.
Instead, you need to associate the different behaviors your want with each instance, like so:
class C a where
toString :: a -> String
draw :: a -> String
instance C MyType1 where
toString v = "MyType1"
draw (MyObj11 x) = "11"
draw (MyObj12 x y) = "12"
instance C MyType2 where
toString v = "MyType2"
draw (MyObj22 x y) = "22"
data MyType1 = MyObj11 Int | MyObj12 Int Int
data MyType2 = MyObj21 Int | MyObj22 Int Int
test :: C a => a -> String
test x = draw x
The branches of your original test function are now distributed amongst the instances.
Some alternative tricks involve using class-associated data types (where you prove to the compiler that a data type is shared amongst all instances), or view patterns (which let you generalize pattern matching).
View patterns
We can use view patterns to clean up the connection between pattern matching and type class instances, a little, allowing us to approximate pattern matching across instances by pattern matching on a shared type.
Here's an example, where we write one function, with two cases, that lets us pattern match against anything in the class.
{-# LANGUAGE ViewPatterns #-}
class C a where
view :: a -> View
data View = One Int
| Two Int Int
data MyType1 = MyObj11 Int | MyObj12 Int Int
instance C MyType1 where
view (MyObj11 n) = One n
view (MyObj12 n m) = Two n m
data MyType2 = MyObj21 Int | MyObj22 Int Int
instance C MyType2 where
view (MyObj21 n) = One n
view (MyObj22 n m) = Two n m
test :: C a => a -> String
test (view -> One n) = "One " ++ show n
test (view -> Two n m) = "Two " ++ show n ++ show m
Note how the -> syntax lets us call back to the right view function in each instance, looking up a custom data type encoding per-type, in order to pattern match on it.
The design challenge is to come up with a view type that captures all the behavior variants you're interested in.
In your original question, you wanted every constructor to have a different behavior, so there's actually no reason to use a view type (dispatching directly to that behavior in each instance already works well enough).

Resources