Creating list of values of the same typeclass but different types - haskell

I'm new to Haskell and trying to do something which I'm sure is easy but I'm not seeing the right way to do it.
What I want is a list of values of a particular typeclass, but different types of that typeclass. Eg:
class Shape a where
area :: a -> Double
numVertices :: a -> Integer
data Triangle = Triangle {...}
data Square = Square {...}
instance Shape Triangle where ...
instance Shape Square where ...
x = [Triangle (...), Square (...)]
I'm getting a compiler error because the list has different types. What's the right way to do what I'm trying to do here? The only thing I've been able to come up with is doing something like this:
data WrappedShape = WrappedShape {
getArea :: () -> Double
, getNumVertices :: () -> Integer
}
wrap s = WrappedShape {
getArea = \ () -> area s
, getNumVertices = \ () -> vertices s
}
x = [wrap (Triangle (...)), wrap (Square (...))]
This works, but it's heavy on boilerplate, since I have to effectively define Shape twice and with differently-named members. What's the standard way to do this sort of thing?

If you just need a few different shapes, you can enumerate each shape as a constructor, here is a example:
data SomeShapes = Triangle {...}
| Square {...}
instance Shape SomeShapes where
area (Triangle x) = ...
area (Square x) = ....
now you can put them in a list, because they are same type of SomeShapes
[Triangle {...}, Square {...}]

Your wrapped type is probably the best idea.
It can be improved by noting that, in a lazy language like Haskell, the type () -> T essentially works like the plain T. You probably want to delay computation and write stuff like let f = \ () -> 1+2 which does not perform addition until the function f is called with argument (). However, let f = 1+2 already does not perform addition until f is really needed by some other expression -- this is laziness.
So, we can simply use
data WrappedShape = WrappedShape {
getArea :: Double
, getNumVertices :: Integer
}
wrap s = WrappedShape {
getArea = area s
, getNumVertices = vertices s
}
x = [wrap (Triangle (...)), wrap (Square (...))]
and forget about passing () later on: when we will access a list element, the area/vertices will be computed (whatever we need). That is print (getArea (head x)) will compute the area of the triangle.
The \ () -> ... trick is indeed needed in eager languages, but in Haskell it is an anti-pattern. Roughly, in Haskell everything has a \ () -> ... on top, roughly speaking, s o there's no need to add another one.
These is another solution to your problem, which is called an "existential type". However, this sometimes turns into an anti-pattern as well, so I do not recommend to use it lightly.
It would work as follows
data WrappedShape = forall a. Shape a => WrappedShape a
x = [WrappedShape (Triangle ...), WrappedShape (Square ...)]
exampleUsage = case head x of WrappedShape s -> area s
This is more convenient when the type class has a lots of methods, since we do not have to write a lot of fields in the wrapped type.
The main downside of this technique is that it involves more complex type machinery, for no real gain. I mean a basic list [(Double, Integer)] has the same functionality of [WrappedShape] (list of existentials), so why bother with the latter?
Luke Palmer wrote about this anti-pattern. I do not agree with that post completely, but I think he does have some good points.
I do not have a clear-cut line where I would start using existentials over the basic approach, but these factors are what I would consider:
How many methods does the type class have?
Are there any methods of the type class where the type a (the one related to the class) appears not only as an argument? E.g. a method foo :: a -> (String, a)?

Related

Existential types in Haskell and generics in other languages

I was trying to grasp the concept of existential types in Haskell using the article Haskell/Existentially quantified types. At the first glance, the concept seems clear and somewhat similar to generics in object oriented languages. The main example there is something called "heterogeneous list", defined as follows:
data ShowBox = forall s. Show s => SB s
heteroList :: [ShowBox]
heteroList = [SB (), SB 5, SB True]
instance Show ShowBox where
show (SB s) = show s
f :: [ShowBox] -> IO ()
f xs = mapM_ print xs
main = f heteroList
I had a different notion of a "heterogeneous list", something like Shapeless in Scala. But here, it's just a list of items wrapped in an existential type that only adds a type constraint. The exact type of its elements is not manifested in its type signature, the only thing we know is that they all conform to the type constraint.
In object-oriented languages, it seems very natural to write something like this (example in Java). This is a ubiquitous use case, and I don't need to create a wrapper type to process a list of objects that all implement a certain interface. The animals list has a generic type List<Vocal>, so I can assume that its elements all conform to this Vocal interface:
interface Vocal {
void voice();
}
class Cat implements Vocal {
public void voice() {
System.out.println("meow");
}
}
class Dog implements Vocal {
public void voice() {
System.out.println("bark");
}
}
var animals = Arrays.asList(new Cat(), new Dog());
animals.forEach(Vocal::voice);
I noticed that existential types are only available as a language extension, and they are not described in most of the "basic" Haskell books or tutorials, so my suggestion is that this is quite an advanced language feature.
My question is, why? Something that seems basic in languages with generics (constructing and using a list of objects whose types implement some interface and accessing them polymorphically), in Haskell requires a language extension, custom syntax and creating an additional wrapper type? Is there no way of achieving something like that without using existential types, or is there just no basic-level use cases for this?
Or maybe I'm just mixing up the concepts, and existential types and generics mean completely different things. Please help me make sense of it.
Yes,existential types and generic mean different things. An existential type can be used similarly to an interface in an object-oriented language. You can put one in a list of course, but a list or any other generic type is not needed to use an interface. It is enough to have a variable of type Vocal to demonstrate its usage.
It is not widely used in Haskell because it is not really needed most of the time.
nonHeteroList :: [IO ()]
nonHeteroList = [print (), print 5, print True]
does the same thing without any language extension.
An existential type (or an interface in an object-oriented language) is nothing but a piece of data with a bundled dictionary of methods. If you only have one method in your dictionary, just use a function. If you have more than one, you can use a tuple or a record of those. So if you have something like
interface Shape {
void Draw();
double Area();
}
you can express it in Haskell as, for example,
type Shape = (IO (), Double)
and say
circle center radius = (drawCircle center radius, pi * radius * radius)
rectangle topLeft bottomRight = (drawRectangle topLeft bottomRight,
abs $ (topLeft.x-bottomRight.x) * (topLeft.y-bottomRight.y))
shapes = [circle (P 2.0 3.5) 4.2, rectangle (P 3.3 7.2) (P -2.0 3.1)]
though you can express exactly the same thing with type classes, instances and existentials
class Shape a where
draw :: a -> IO ()
area :: a -> Double
data ShapeBox = forall s. Shape s => SB s
instance Shape ShapeBox where
draw (SB a) = draw a
area (SB a) = area a
data Circle = Circle Point Double
instance Shape Circle where
draw (Circle c r) = drawCircle c r
area (Circle _ r) = pi * r * r
data Rectangle = Rectangle Point Point
instance Shape Rectangle where
draw (Rectangle tl br) = drawRectangle tl br
area (Rectangle tl br) = abs $ (tl.x - br.x) * (tl.y - br.y)
shapes = [Circle (P 2.0 3.5) 4.2, Rectangle (P 3.3 7.2) (P -2.0 3.1)]
and there you have it, N times longer.
is there just no basic-level use cases for this?
Sort-of, yeah. While in Java, you have no choice but to have open classes, Haskell has ADTs which you'd normally use for these kind of use-cases. In your example, Haskell can represent it in one of two ways:
data Cat = Cat
data Dog = Dog
class Animal a where
voice :: a -> String
instance Animal Cat where
voice Cat = "meow"
instance Animal Dog where
voice Dog = "woof"
or
data Animal = Cat | Dog
voice Cat = "meow"
voice Dog = "woof"
If you needed something extensible, you'd use the former, but if you need to be able to case on the type of animal, you'd use the latter. If you wanted the former, but wanted a list, you don't have to use existential types, you could instead capture what you wanted in a list, like:
voicesOfAnimals :: [() -> String]
voicesOfAnimals = [\_ -> voice Cat, \_ -> voice Dog]
Or even more simply
voicesOfAnimals :: [String]
voicesOfAnimals = [voice Cat, voice Dog]
This is kind-of what you're doing with Heterogenous lists anyway, you have a constraint, in this case Animal a on each element, which lets you call voice on each element, but nothing else, since the constraint doesn't give you any more information about the value (well if you had the constraint Typeable a you'd be able to do more, but let's not worry about dynamic types here).
As for the reason for why Haskell doesn't support Heterogenous lists without extensions and wrappers, I'll let someone else explain it but key topics are:
subtyping
variance
inference
https://gitlab.haskell.org/ghc/ghc/-/wikis/impredicative-polymorphism (I think)
In your Java example, what's the type of Arrays.asList(new Cat())? Well, it depends on what you declare it as. If you declare the variable with List<Cat>, it typechecks, you can declare it with List<Animal>, and you can declare it with List<Object>. If you declared it as a List<Cat>, you wouldn't be able to reassign it to List<Animal> as that would be unsound.
In Haskell, typeclasses can't be used as the type within a list (so [Cat] is valid in the first example and [Animal] is valid in the second example, but [Animal] isn't valid in the first example), and this seems to be due to impredicative polymorphism not being supported in Haskell (not 100% sure). Haskell lists are defined something like [a] = [] | a : [a]. [x, y, z] is just syntatic sugar for x : (y : (z : [])). So consider the example in Haskell. Let's say you type [Dog] in the repl (this is equivalent to Dog : [] btw). Haskell infers this to have the type [Dog]. But if you were to give it Cat at the front, like [Cat, Dog] (Cat : Dog : []), it would match the 2nd constructor (:), and would infer the type of Cat : ... to [Cat], which Dog : [] would fail to match.
Since others have explained how you can avoid existential types in many cases, I figured I'd point out why you might want them. The simplest example I can think of is called Coyoneda:
data Coyoneda f a = forall x. Coyoneda (x -> a) (f x)
Coyoneda f a holds a container (or other functor) full of some type x and a function that can be mapped over it to produce an f a. Here's the Functor instance:
instance Functor (Coyoneda f) where
fmap f (Coyoneda g x) = Coyoneda (f . g) x
Note that this does not have a Functor f constraint! What makes it useful? To explain that takes two more functions:
liftCoyoneda :: f a -> Coyoneda f a
liftCoyoneda = Coyoneda id
lowerCoyoneda :: Functor f => Coyoneda f a -> f a
lowerCoyoneda (Coyoneda f x) = fmap f x
The cool thing is that fmap applications get built up and performed all together:
lowerCoyoneda . fmap f . fmap g . fmap h . liftCoyoneda
is operationally
fmap (f . g . h)
rather than
fmap f . fmap g . fmap h
This can be useful if fmap is expensive in the underlying functor.

Writing modules in Haskell the right way

(I'm totally rewriting this question to give it a better focus; you can see the history of changes if you want to see the original.)
Let's say I have two modules:
One module defines the function inverseAndSqrt. What this function actually does is not important; what is important is that it returns none, one, or both of two things in a way that the client can distinguish which one is which;
module Module1 (inverseAndSqrt) where
type TwoOpts a = (Maybe a, Maybe a)
inverseAndSqrt :: Int -> TwoOpts Float
inverseAndSqrt x = (if x /= 0 then Just (1.0/(fromIntegral x)) else Nothing,
if x >= 0 then Just (sqrt $ fromIntegral x) else Nothing)
another module defines other functions depending on inverseAndSqrt and on its type
module Module2 where
import Module1
fun :: (Maybe Float, Maybe Float) -> Float
fun (Just x, Just y) = x + y
fun (Just x, Nothing) = x
fun (Nothing, Just y) = y
exportedFun :: Int -> Float
exportedFun = fun . inverseAndSqrt
What I want to understand from the perspective of design principle is: how should I interface Module1 with other modules (e.g. Module2) in a way that makes it well encapsulated, reusable, etc?
The problems I see are
I could one day decide that I don't want to use a pair to return the two results anymore; I could decide to use a 2 elements list; or another type which is isomorphic (I think this is the right adjective, isn't it?) to a pair; if I do this, all client code will break
Exporting the TwoOpts type synonym doesn't solve anything, as Module1 could still change its implementation thus breaking client code.
Module1 is also forcing the type of the two optionals to be the same, but I'm not sure this is really relevant to this question...
How should I design Module1 (and thus edit Module2 as well) such that the two are not tightly coupled?
One thing I can think of is that maybe I should define a typeclass expressing what "a box with two optional things in it" is, and then Module1 and Module2 would use that as a common interface. But should that be in both module? In either of them? Or in none of them, in a third module? Or maybe such a class/concept is not needed?
I'm not a computer scientist so I'm sure that this question highlights some misunderstanding of mine due to lack of experience and theoretical background. Any help filling the gaps is welcome.
Possible modifications I'd like to support
Related to what chepner suggested in a comment to his answer, at some point I might want to extend the support from 2-tuple things to both 2- and 3-tuple things, having different accessor names for them, suche as get1of2/get2of2 (let's say these are the name we use when we first design Module1) vs get1of3/get2of3/get3of3.
At some point I would also be able to complement this 2-tuple-like type with something else, for instance an optional containing Just the sum¹ of the two main contents only if they are both Justs, or a Nothing if at least one of the two main contents is a Nothing. I guess in this case the internal representation of this class would be something like ((Maybe a, Maybe a), Maybe b) (¹ The sum is really a stupid example, so I've used b here instead of a to be more general than the sum would require).
To me, Haskell design is all type-centric. The design rule for functions is just "use the most general and accurate types that do the job", and the whole problem of design in Haskell is about coming up with the best types for the job.
We would like there to be no "junk" in the types, so that they have exactly one representation for each value you want to denote. E.g. String is a bad representation for numbers, because "0", "0.0", "-0" all mean the same thing, and also because "The Prisoner" is not a number -- it is a valid representation that does not have a valid denotation. If, say for performance reasons, the same denotation can be represented multiple ways, the type's API should make that difference invisible to the user.
So in your case, (Maybe a, Maybe a) is perfect -- it means exactly what you need it to mean. Using something more complicated is unnecessary, and will just complicate matters for the user. At some point whatever you expose will have to be convertible to a Maybe a for the first thing and a Maybe a for the second thing, and there is no extra information than that, so the tuple is perfect. Whether you use a type synonym or not is a matter of style -- I prefer not use synonyms at all and only give types names when I have a more formal abstraction in mind.
Connotation is important. For example, if I had a function for finding the roots of a quadratic polynomial, I probably wouldn't use TwoOpts, even though there are at most two of them. The fact that my return values are all "the same kind of thing" in an intuitive sense makes me prefer a list (or if I'm feeling particularly picky, a Set or Bag), even if the list has at most two elements. I just have it match my best understanding of the domain at the time, so I won't change it unless my understanding of the domain has changed in a significant way, in which case the opportunity to review all its uses is exactly what I want. If you are writing your functions to be as polymorphic as possible, then often you won't need to change anything but the specific moments the meaning is used, the exact moment domain knowledge is required (such as understanding the relationship between TwoOpts and Set). You don't need to "redo the plumbing" if it's made of a sufficiently flexible, polymorphic material.
Supposing you didn't have a clean isomorphism to a standard type like (Maybe a, Maybe a), and you wanted to formalize TwoOpts. The way here is to build an API out of its constructors, combinators, and eliminators. For example:
data TwoOpts a -- abstract, not exposed
-- constructors
none :: TwoOpts a
justLeft :: a -> TwoOpts a
justRight :: a -> TwoOpts a
both :: a -> a -> TwoOpts a
-- combinators
-- Semigroup and Monoid at least
swap :: TwoOpts a -> TwoOpts a
-- eliminators
getLeft :: TwoOpts a -> Maybe a
getRight :: TwoOpts a -> Maybe a
In this case the eliminators give exactly your representation (Maybe a, Maybe a) as their final coalgebra.
-- same as the tuple in a newtype, just more conventional
data TwoOpts a = TwoOpts (Maybe a) (Maybe a)
Or if you wanted to focus on the constructors side you could use an initial algebra
data TwoOpts a
= None
| JustLeft a
| JustRight a
| Both a a
You are at liberty to change this representation as long as it still implements the combinatory API above. If you have reason to use different representations of the same API, make the API into a typeclass (typeclass design is a whole other story).
In Einstein's famous words, "make it as simple as possible, but no simpler".
Don't define a simple type alias; this exposes the details of how you implement TwoOpts.
Instead, define a new type, but don't export the data constructor, but rather functions for accessing the two components. Then you are free to change the implementation of the type all you like without changing the interface, because the user can't pattern-match on a value of type TwoOpts a.
module Module1 (TwoOpts, inverseAndSqrt, getFirstOpt, getSecondOpt) where
data TwoOpts a = TwoOpts (Maybe a) (Maybe a)
getFirstOpt, getSecondOpt :: TwoOpts a -> Maybe a
getFirstOpt (TwoOpts a _) = a
getSecondOpt (TwoOpts _ b) = b
inverseAndSqrt :: Int -> TwoOpts Float
inverseAndSqrt x = TwoOpts (safeInverse x) (safeSqrt x)
where safeInverse 0 = Nothing
safeInverse x = Just (1.0 / fromIntegral x)
safeSqrt x | x >= 0 = Just $ sqrt $ fromIntegral x
| otherwise = Nothing
and
module Module2 where
import Module1
fun :: TwoOpts Float -> Float
fun a = case (getFirstOpts a, getSecondOpt a) of
(Just x, Just y) -> x + y
(Just x, Nothing) -> x
(Nothing, Just y) -> y
exportedFun :: Int -> Float
exportedFun = fun . inverseAndSqrt
Later, when you realize that you've reimplemented the type product, you can change your definitions without affecting any user code.
newtype TwoOpts a = TwoOpts { getOpts :: (Maybe a, Maybe a) }
getFirstOpt, getSecondOpt :: TwoOpts a -> Maybe a
getFirstOpt = fst . getOpts
getSecondOpt = snd . getOpts

Design pattern for subclass-like structure

My goal is to represent a set of types with a similar behaviour in a elegant and performant manner. To achieve this, I have created a solution that utilises a single type, followed by a set of functions that perform pattern matching.
My first question is: is there a way how to represent the same ideas using a single type-class and instead of having a constructor per each variation to have a type that implements said type-class?
Which of the two approaches below is:
- a better recognised design pattern in Haskell?
- more memory efficient?
- more performant?
- more elegant and why?
- easier to use for consumers of the code?
Approach 1: Single type and pattern matching
Suppose there is a following structure:
data Aggregate a
= Average <some necessary state keeping>
| Variance <some necessary state keeping>
| Quantile a <some necessary state keeping>
It's constructors are not public as that would expose the internal state keeping. Instead, a set of constructor functions exist:
newAverage :: Floating a
=> Aggregate a
newAverage = Average ...
newVariance :: Floating a
=> Aggregate a
newVariance = Variance ...
newQuantile :: Floating a
=> a -- ! important, a parameter to the function
-> Aggregate a
newQuantile p = Quantile p ...
Once the object is created, we can perform two functions: put values into it, and once we are satisfied, we can get the current value:
get :: Floating a
=> Aggregate a
-> Maybe a
get (Average <state>) = getAverage <state>
get (Variance <state>) = getVariance <state>
get (Quantile _ <state>) = getQuantile <state>
put :: Floating a
=> a
-> Aggregate a
-> Aggregate a
put newVal (Average <state>) = putAverage newVal <state>
put newVal (Variance <state>) = putVariance newVal <state>
put newVal (Quantile p <state>) = putQuantile newVal p <state>
Approach 2: Type-classes and instances
class Aggregate a where
new :: a
get :: Floating f => a f -> Maybe f
put :: Floating f =>
data Average a = Average Word64 a
data Variance a ...
instance Aggregate Average where
instance Aggregate Variance where
instance Aggregate Quantile where
The obvious problem here is the fact that new is not parametric and thus Quantile can't be initialised with the p parameter. Adding a parameter to new is possible, but it would result in all other non-parametric constructors to ignore the value, which is not a good design.
You are missing the "codata" encoding, which sounds like it might be the best fit for your problem.
data Aggregate a = Aggregate
{ get :: Maybe a
, put :: a -> Aggregate a
}
-- Use the closure to keep track of local state.
newAverage :: (Floating a) => Aggregate a
newAverage = Aggregate { get = Nothing, put = go 0 0 }
where
go n total x = Aggregate { get = Just ((total + x) / (n+1))
, put = go (n+1) (total+x)
}
-- Parameters are not a problem.
newQuantile :: (Floating a) => a -> Aggregate a
newQuantile p = Aggregate { get = ... ; put = \x -> ... }
...
For some reason this approach always slips under the radar of people with OO backgrounds, which is strange because it is a pretty close match to that paradigm.
It's hard to give a general recommendation. I tend to prefer approach 1. Note that you could use
data Aggregate a
= Average AverageState
| Variance VarianceState
| Quantile a QuantileState
and export every constructor above, keeping only the ...State types private to the module.
This might be feasible in some contexts, but not in others, so it has to be evaluated on a case by case basis.
About approach 2, this could be more convenient if you have many constructors / types around. To fix the new problem, one could use type families (or fundeps) as in
class Floating f => Aggregate a f where
type AggregateNew a f
new :: AggregateNew a f -> a f
get :: a f -> Maybe f
put :: ...
instance Floating f => Aggregate Average f where
type AggregateNew (Average a) f = ()
new () = ...
instance Floating f => Aggregate Quantile f where
type AggregateNew (Quantile a) f = a
new x = ...
The naming above is horrible, but I used it to make the point. new takes an argument of type AggregateNew k f which can be () if new needs no information, or some more informative type when it is needed, like a for creating a Quantile.
There is a third approach for defining “aggregators” that neither requires an inextensible sum type nor multiple datatypes + a typeclass.
Approach 3: Single-constructor datatype that puts state behind an existential
Consider this type:
{-# LANGUAGE ExistentialQuantification #-}
data Fold a b = forall x. Fold (x -> a -> x) x (x -> b)
It represents an aggregator that ingests values of type a and eventually "returns" a value of type b while carrying an internal state x.
The constructor has type (x -> a -> x) -> x -> (x -> b) -> Fold a b. It takes a step function, an initial state and a final "summary" function. Notice that the state is decoupled from the return value b. They can be the same, but it's not required.
Also, the state is existentially quantified. We know the type of the state when we create a Fold, and we can work with it when we pattern-match on the Fold—in order to feed it data through the step function—but it's not reflected in the Fold's type. We can put Fold values with different inner states in the same container without problems, as long as they ingest and return the same types.
This pattern is sometimes called a “beautiful fold”. There is a library called foldl that is based on it, and provides may premade folds and utility functions.
The Fold type in foldl has many useful instances. In particular, the Applicative instance lets us create composite folds that still traverse the input data a single time, instead of requiring multiple passes.

Is it bad form to make new types/datas for clarity? [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 3 years ago.
Improve this question
I would like to know if it is bad form to do something like this:
data Alignment = LeftAl | CenterAl | RightAl
type Delimiter = Char
type Width = Int
setW :: Width -> Alignment -> Delimiter -> String -> String
Rather than something like this:
setW :: Int -> Char -> Char -> String -> String
I do know that remaking those types effectively does nothing but take up a few lines in exchange for clearer code. However, if I use the type Delimiter for multiple functions, this would be much clearer to someone importing this module, or reading the code later.
I am relatively new to Haskell so I do not know what is good practice for this type of stuff. If this is not a good idea, or there is something that would improve clarity that is preferred, what would that be?
You're using type aliases, they only slightly help with code readability. However, it's better to use newtype instead of type for better type-safety. Like this:
data Alignment = LeftAl | CenterAl | RightAl
newtype Delimiter = Delimiter { unDelimiter :: Char }
newtype Width = Width { unWidth :: Int }
setW :: Width -> Alignment -> Delimiter -> String -> String
You will deal with extra wrapping and unwrapping of newtype. But the code will be more robust against further refactorings. This style guide suggests to use type only for specializing polymorphic types.
I wouldn't consider that bad form, but clearly, I don't speak for the Haskell community at large. The language feature exists, as far as I can tell, for that particular purpose: to make the code easier to read.
One can find examples of the use of type aliases in various 'core' libraries. For example, the Read class defines this method:
readList :: ReadS [a]
The ReadS type is just a type alias
type ReadS a = String -> [(a, String)]
Another example is the Forest type in Data.Tree:
type Forest a = [Tree a]
As Shersh points out, you can also wrap new types in newtype declarations. That's often useful if you need to somehow constrain the original type in some way (e.g. with smart constructors) or if you want to add functionality to a type without creating orphan instances (a typical example is to define QuickCheck Arbitrary instances to types that don't otherwise come with such an instance).
Using newtype—which creates a new type with the same representation as the underlying type but not substitutable with it— is considered good form. It's a cheap way to avoid primitive obsession, and it's especially useful for Haskell because in Haskell the names of function arguments are not visible in the signature.
Newtypes can also be a place on which to hang useful typeclass instances.
Given that newtypes are ubiquitous in Haskell, over time the language has gained some tools and idioms to manipulate them:
Coercible A "magical" typeclass that simplifies conversions between newtypes and their underlying types, when the newtype constructor is in scope. Often useful to avoid boilerplate in function implementations.
ghci> coerce (Sum (5::Int)) :: Int
ghci> coerce [Sum (5::Int)] :: [Int]
ghci> coerce ((+) :: Int -> Int -> Int) :: Identity Int -> Identity Int -> Identity Int
ala. An idiom (implemented in various packages) that simplifies the selection of a newtype that we might want to use with functions like foldMap.
ala Sum foldMap [1,2,3,4 :: Int] :: Int
GeneralizedNewtypeDeriving. An extension for auto-deriving instances for your newtype based on instances available in the underlying type.
DerivingVia A more general extension, for auto-deriving instances for your newtype based on instances available in some other newtype with the same underlying type.
One important thing to note is that Alignment versus Char is not just a matter of clarity, but one of correctness. Your Alignment type expresses the fact that there are only three valid alignments, as opposed to however many inhabitants Char has. By using it, you avoid trouble with invalid values and operations, and also enable GHC to informatively tell you about incomplete pattern matches if warnings are turned on.
As for the synonyms, opinions vary. Personally, I feel type synonyms for small types like Int can increase cognitive load, by making you track different names for what is rigorously the same thing. That said, leftaroundabout makes a great point in that this kind of synonym can be useful in the early stages of prototyping a solution, when you don't necessarily want to worry about the details of the concrete representation you are going to adopt for your domain objects.
(It is worth mentioning that the remarks here about type largely don't apply to newtype. The use cases are different, though: while type merely introduces a different name for the same thing, newtype introduces a different thing by fiat. That can be a surprisingly powerful move -- see danidiaz's answer for further discussion.)
Definitely is good, and here is another example, supose you have this data type with some op:
data Form = Square Int | Rectangle Int Int | EqTriangle Int
perimeter :: Form -> Int
perimeter (Square s) = s * 4
perimeter (Rectangle b h) = (b * h) * 2
perimeter (EqTriangle s) = s * 3
area :: Form -> Int
area (Square s) = s ^ 2
area (Rectangle b h) = (b * h)
area (EqTriangle s) = (s ^ 2) `div` 2
Now imagine you add the circle:
data Form = Square Int | Rectangle Int Int | EqTriangle Int | Cicle Int
add its operations:
perimeter (Cicle r ) = pi * 2 * r
area (Cicle r) = pi * r ^ 2
it is not very good right? Now I want to use Float... I have to change every Int for Float
data Form = Square Double | Rectangle Double Double | EqTriangle Double | Cicle Double
area :: Form -> Double
perimeter :: Form -> Double
but, what if, for clarity and even for reuse, I use type?
data Form = Square Side | Rectangle Side Side | EqTriangle Side | Cicle Radius
type Distance = Int
type Side = Distance
type Radius = Distance
type Area = Distance
perimeter :: Form -> Distance
perimeter (Square s) = s * 4
perimeter (Rectangle b h) = (b * h) * 2
perimeter (EqTriangle s) = s * 3
perimeter (Cicle r ) = pi * 2 * r
area :: Form -> Area
area (Square s) = s * s
area (Rectangle b h) = (b * h)
area (EqTriangle s) = (s * 2) / 2
area (Cicle r) = pi * r * r
That allows me to change the type only changing one line in the code, supose I want the Distance to be in Int, I will only change that
perimeter :: Form -> Distance
...
totalDistance :: [Form] -> Distance
totalDistance = foldr (\x rs -> perimeter x + rs) 0
I want the Distance to be in Float, so I just change:
type Distance = Float
If I want to change it to Int, I have to make some adjustments in the functions, but thats other issue.

How can I perform a scatter/gather operation on types in Haskell?

I have tree that hold contains nodes of different types. These are tagged using a datatype:
data Wrapping = A Int
| B String
I want to write two functions:
scatter :: Wrapping -> a
gather :: a -> Output
The idea is that I can use (scatter.gather) :: Wrapping -> Output. There will of course be several different variations on both the scatter and the gather function (with each scatter variant having a unique Wrappingn datatype, but the set of intermediate types will always be the same) and I want to be able to cleanly compose them.
The issue that I have is that the type parameter a is not really free, it is a small explicit set of types (here it is {Int,String}). If I try to encode what I have so far into Haskell typeclasses then I get to:
{-# LANGUAGE FlexibleInstances #-}
data Wrapping = A Int | B String
class Fanin a where
gather :: a -> String
instance Fanin Int where
gather x = show x
instance Fanin String where
gather x = x
class Fanout a where
scatter :: Fanout a => Wrapping -> a
instance Fanout Int where
scatter (A n) = n
instance Fanout String where
scatter (B x) = x
combined = gather.scatter
The two classes typecheck fine but obviously the final line throws errors because ghc knows that the type parameters do match on every case, only on the two that I have defined. I've tried various combinations of extending one class from the other:
class Fanin a => Fanout a where ...
class Fanout a => Fanin a where ...
Finally I've looked at GADTs and existential types to solve this but I am stumbling around in the dark. I can't find a way to express a legal qualified type signature to GHC, where I've tried combinations of:
{-# LANGUAGE RankNTypes #-}
class (forall a. Fanout a) => Fanin a where
class (forall a. Fanin a) => Fanout a where
Question: how do I express to GHC that I want to restrict a to only the two types in the set?
I get the feeling that the solution lies in one of the techniques that I've looked at but I'm too lost to see what it is...
The idea is that I can use (scatter.gather) :: Wrapping -> Output.
There will of course be several different variations on both the
scatter and the gather function (with each scatter variant having a
unique Wrappingn datatype, but the set of intermediate types will
always be the same) and I want to be able to cleanly compose them.
If I understand correctly, you'd like to have different Wrapping types but the intermediate a type is constantly Either Int String. We can just reflect this information in our classes:
data Wrapping = A Int
| B String
class Fanout wrap where
scatter :: wrap -> Either Int String
instance Fanout Wrapping where
scatter (A n) = Left n
scatter (B str) = Right str
class Fanin output where
gather :: Either Int String -> output
instance Fanin String where
gather = either show id
combined :: Wrapping -> String
combined = gather . scatter
Also, this use case doesn't seem especially amenable to type classes, from what I can glean from the question. In particular, we can get rid of Fanin, and then combined = either show id . scatter looks better to my eyes than the previous definition.
The type class solution makes sense here only if just a single Either Int String -> a or a -> Either Int String function makes sense for each a, and you'd like to enforce this.
If I understand you correctly you need something like the following:
module Main ( main ) where
-- Different kinds of wrapper data types
data WrapperA = A Int | B String
data WrapperB = C Int | D Float
-- A single intermediate data type (with phantom type)
data Intermediate a = E Int | F String
-- Generic scatter and gather functions
class Wrapped a where
scatter :: Wrapped a => a -> Intermediate a
gather :: Wrapped a => Intermediate a -> String
-- Specific scatter and gather implementations
instance Wrapped WrapperA where
scatter (A i) = E i
scatter (B s) = F s
gather (E i) = show i
gather (F s) = s
instance Wrapped WrapperB where
scatter (C i) = E i
scatter (D f) = F $ show f
gather (E i) = show i
gather (F s) = s ++ " was a float"
-- Beautiful composability
combined :: Wrapped a => a -> String
combined = gather . scatter
wrapperAexample1 = A 10
wrapperAexample2 = B "testing"
wrapperBexample1 = C 11
wrapperBexample2 = D 12.4
main :: IO ()
main = do print $ combined wrapperAexample1
print $ combined wrapperAexample2
print $ combined wrapperBexample1
print $ combined wrapperBexample2
The main issue seems to be that you have an intermediate type which can have different kinds of content, but this is constant for different wrappers. Still, depending on the kind of wrapper, you want the gather function to behave differently.
To do this, I would define the Intermediate type to specify the kinds of values that can be held in the intermediate stage, and give it a phantom type parameter (to remember what kind of wrapper it originated from). You can then define a class to hold the scatter and gather functions, and define these differently for different kinds of wrappers.
The code above compiles without errors for me, and gives the following output:
"10"
"testing"
"11"
"12.4 was a float"
As you can see, the WrapperB/D Float input is treated differently than the WrapperA/B String (it is tagged as a float value, even after it has been converted to a String). This is because in the Intermediate representation it is remembered that the origin is a WrapperB: the one is of type Intermediate WrapperA, the other of Intermediate WrapperB.
If, on the other hand, you don't actually want the gather function to behave differently for different wrappers, you can simply take that out of the class and take out the phantom type. The easiest way to let ghc know that the type in the intermediate stage can be Int or String seems to me to still define something like the Intermediate type, rather than use just a.

Resources