How to make function useable only for a certain data constructor of an ADT? - haskell

I'm currently playing around with ADTs in Haskell and try to build an ADT Figure:
data Figure = Rect { x :: Integer, y :: Integer, width :: Integer, height :: Integer}
| Circle { x :: Integer, y :: Integer, radius :: Integer}
| CombiFigure Figure Figure
deriving (Eq, Show, Read)
Now I came across the question how to implement a function that should not accept every Figure, but e.g. only a Circle.
Do I already have a bad design? Or is there some best-practice how to do this?
As example, think about a diameter function. All that came to my mind (I'm a complete beginner in Haskell) are the following two options, using undefined or Maybe:
1:
diameter :: Figure -> Integer
diameter (Circle _ _ r) = 2 * r
diameter _ = undefined
2:
diameter :: Figure -> Maybe Integer
diameter (Circle _ _ r) = Just (2 * r)
diameter _ = Nothing
Are there more preferable ways on how to accomplish that?
Thanks!

You are correct that there is something not right here. The best way of thinking about it would be to start at the function diameter and decide what it's type should ideally be. You would likely come up with
diameter :: Circle -> Integer
diameter (Circle _ _ r) = 2 * r
because diameters are only defined for circles.
This means that you will have to augment your data structure by splitting out Circle (and Rect too):
data Figure = RectFigure Rect
| CircleFigure Circle
| CombiFigure Figure Figure
deriving (Eq, Show, Read)
data Rect = Rect { rectX :: Integer, rectY :: Integer, rectWidth :: Integer, height :: Integer}
deriving (Eq, Show, Read)
data Circle = Circle { circleX :: Integer, circleY :: Integer, circleRadius :: Integer}
deriving (Eq, Show, Read)
which is nice because it is now more flexible: you can write functions that don't care what Figure they are applied to, and you can write functions that are defined on specific Figures.
Now, if we are in a higher-up function and have a reference to a Figure and we want to compute its diameter if it's a CircleFigure, then you can use pattern matching to do this.
Note: using undefined or exceptions (in pure code) is likely a code smell. It could probably be solved by rethinking your types. If you have to indicate failure, then use Maybe/Either.

Your type definition by itself (i.e data Figure = ...) is introducing partial functions. e.g. even though width is of type width :: Figure -> Integer it can only work on Rect values:
\> width $ Rect 1 2 3 4
3
\> width $ Circle 1 2 3
*** Exception: No match in record selector width
so, you already have defined functions which can work on one figure but not another (similar to diameter function in the question).
That said, a 3rd solution would be to define Circle, Rectangle etc, as separate types; then, define a Figure type class which defines the common interface of these types.
class Figure a where
area, perimeter :: a -> Double
instance Figure Circle where
area = ...
perimeter = ...
Additionally each type may have their own exclusive functions. Or, you may add more interfaces, (i.e. type classes) which cover some but not all the figure types.
An advantage of type classes is that they are easier to extend; e.g. if one wants to add, say, a Triangle type later on, he can opt-in any type class which applies to a triangle, and define an instance only for those type classes.
Whereas in the data Figure = ... approach, you need to find every function which can take a Figure as argument and make sure it will handle a Triangle as well. If you are shipping a library then you do not have access to all these functions.
>> for the reference, there was a similar recent discussion of data declaration vs type classes on haskell cafe mailing list.

Related

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.

Algebraic Types - Haskell

I am working around with algebraic types in Haskell, doing some exercises from a worksheet. I got the following exercises:
Define an algebraic type Point for representing (the coordinates of) points in twodimensional space.
My code for this exercise:
data Point = Point Float Float
deriving (Show)
Using Point, define a modified version PositionedShape of the Shape data type which
includes the centre point of a shape, in addition to its dimensions.
Shape data previously defined:
data Shape = Circle Float |
Rectangle Float Float
deriving (Show)
My code for this exercise:
data PositionedShape = PositionedShape Shape Point
deriving (Show)
Now my question comes in this one:
Define a function:
haskell move :: PositionedShape -> Float -> Float -> PositionedShape
which moves a shape by the given x and y distances
My implementation for this was the following:
move :: PositionedShape -> Float -> Float -> PositionedShape
move (Shape (Point x y)) newX newY = Shape (Point newX newY)
This is returning me this error:
Week8.hs:103:7: error: Not in scope: data constructor ‘Shape’
Failed, modules loaded: none.
Can someone please explain me why this error and how can I solve it? I am getting a bit confused with algebraic types, I tried a lot of things but it just seems I can't get a solution.
You need to pattern match on data constructors (like Circle and Rectangle), not on type constructors as you're trying to do now (like Shape). For PositionedShape, they happen to have the same name, although you completely forgot the match on this one (and in fact, you don't need to care about the inner Shape at all except to copy it through). Also, move is meant to move the shape by the given distances, not to move it to a new given position;.
You need to use the PositionedShape constructor to break apart a PositionedShape You have used the Shape constructor instead.
Try starting with:
move (PositionedShape shape (Point old_x old_y)) [...]
How about
move (PointedShape s (Point x y)) dx dy = PointedShape s (Point (x+dx) (y+dy))

Creating list of values of the same typeclass but different types

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

Iterating over custom data types in Haskell

I have a custom data type that looks like this:
data Circle = Circle
{ radius :: Float
, xPosition :: Float
, yPosition :: Float
}
I want to be able to write a scale function that can take a given circle and change its size like this:
aCircle = Circle 1.5 1 1
scaleFn aCircle 10
The desired output for this example with scale of 10 would be:
Circle 15 10 10
How can I create a function where I can iterate over each field and multiple the values by a constant? In my actual use case I need a way to map over all the fields as there are many of them.
Scaling by a factor is generally a vector space operation. You could do the following:
{-# LANGUAGE TypeFamilies, DeriveGeneric #-}
import Data.VectorSpace
import GHC.Generics (Generic)
data Circle = Circle
{ radius :: Float
, xPosition :: Float
, yPosition :: Float
} deriving (Generic, Show)
instance AdditiveGroup Circle
instance VectorSpace Circle where
type Scalar Circle = Float
main = print $ Circle 1.5 1 1 ^* 10
(result: Circle {radius = 15.0, xPosition = 10.0, yPosition = 10.0}).
(requires vector-space >= 0.11, which has just added support for generic-derived instances.)
However I should remark that Circle as such is not really a good VectorSpace instance: adding two circles doesn't make any sense, and scaling by a negative factor gives a bogus radius. Only define such an instance if your real use case follows the actual vector space axioms.
What you really want for a type like Circle is something like diagrams' Transformable class. But I don't think there's any automatic way to derive an instance for that. In fact, since diagrams has – unfortunately IMO – switched from vector-space to linear, something like this has become considerably tougher to do even in principle.
You can use "scrap your boilerplate":
import Data.Generics
data Circle = Circle
{ radius :: Float
, xPosition :: Float
, yPosition :: Float
}
deriving (Show, Data)
circleModify :: (Float -> Float) -> Circle -> Circle
circleModify f = gmapT (mkT f)
Intuitively, above, mkT f transforms f into a function which is applicable to any type: if the argument of mkT f is a Float, then f is applied, otherwise the argument is returned as it is.
The newly constructed general function is called a "transformation": the T in mkT stands for that.
Then, gmapT applies the transformation mkT f to all the fields of the circle. Note that is a field contained, say, (Float, Bool) that float would be unaffected. Use everywhere instead of gmapT to recursively go deeper.
Note that I'm not a big fan of this approach. If for any reason you change the type of a field, that change will not trigger a type error but gmapT (mkT ...) will now simply skip over that field.
Generic programming can be convenient, but sometimes a bit too much, in that type errors can be silently transformed into unexpected results at runtime. Use with care.

Creating figures in Haskell

Im trying to create datatype "Figure" in Haskell, this datatype should have multiple values:
Square (with parameter length)
Triangle (with parameter length)
Circle (with parameter radius)
Every figure should have a color as well (let's say black and white), this is how my code currently looks like but this doesn't work.
Can anyone help me?
class Figure_ a where
perimeter :: a -> Double
area :: a -> Double
data Figure = forall a. Figure_ a => Figure a
type Radius = Double
type Side = Double
type Color = String
data Circle = Circle Radius
data Triangle = Triangle Side
data Square = Square Side
instance Figure_ Circle where
perimeter (Circle r) = 2 * pi * r
area (Circle r) = pi * r * r
instance Figure_ Triangle where
perimeter (Triangle x y) = 2*(x + y)
area (Triangle x y) = x * y
instance Figure_ Square where
perimeter (Square s) = 4*s
area (Square s) = s*s
instance Figure_ Figure where
perimeter (Figure shape) = perimeter shape
For forall use:
{-# LANGUAGE ExistentialQuantification #-}
Also, your triangle is accepting two parameters, so it should be like this:
data Triangle = Triangle Side Side
But looking at the formula of your Triangle, I think you seem to be confusing it with Rectangle. Also, indentation of your code doesn't seem to be correct. Your code should look like this:
class Figure_ a where
perimeter :: a -> Double
area :: a -> Double
The same indentation rule should also be followed when you create instance of that typeclass.
Im trying to create datatype "Figure" in Haskell, this datatype should have multiple values:
Square (with parameter length)
...
Circle (with parameter radius)
I'm leaving out the triangle to avoid thinking about geometry. Anyway, the questions sounds like you might want the following:
data Figure = Square Double | ... | Circle Double
And then you can define functions like:
area :: Figure -> Double
area (Square side) = side * side
area ... = ...
area (Circle radius) = pi * radius * radius
If you only need one datatype, you don't need classes in Haskell. Haskell classes are for having more than one datatype when they all support a common interface.
There is one reason to prefer your construction with class and forall over the otherwise simpler version with just data: In your version, it is easier to add another kind of figure that was not planned for. If you feel you need this, you might want to read about the existential typeclass antipattern. But if you're just trying to represent figures in Haskell at all, I would certainly start with a simple datatype.

Resources