From f27686677752dca4d715f796aa0cc759bc05f998 Mon Sep 17 00:00:00 2001 From: David Sampson Date: Mon, 4 Nov 2019 10:51:48 -0600 Subject: Info on typeclasses and types --- haskell.html.markdown | 149 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 145 insertions(+), 4 deletions(-) (limited to 'haskell.html.markdown') diff --git a/haskell.html.markdown b/haskell.html.markdown index 90d47c27..f3b84bdd 100644 --- a/haskell.html.markdown +++ b/haskell.html.markdown @@ -293,7 +293,13 @@ foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16 -- 7. Data Types ---------------------------------------------------- --- Here's how you make your own data type in Haskell +-- A data type is declared with a 'type constructor' on the left +-- and one or more 'data constructors' on the right, separated by +-- the pipe | symbol. This is a sum/union type. Each data constructor +-- is a (possibly nullary) function that creates an object of the type +-- named by the type constructor. + +-- This is essentially an enum data Color = Red | Blue | Green @@ -304,7 +310,57 @@ say Red = "You are Red!" say Blue = "You are Blue!" say Green = "You are Green!" --- Your data types can have parameters too: +-- Note that the type constructor is used in the type signature +-- and the data constructors are used in the body of the function +-- Data constructors are primarily pattern-matched against + +-- This next one is a traditional container type holding two fields +-- In a type declaration, data constructors take types as parameters +-- Data constructors can have the same name as type constructors +-- This is common where the type only has a single data constructor + +data Point = Point Float Float + +-- This can be used in a function like: + +distance :: Point -> Point -> Float +distance (Point x y) (Point x' y') = sqrt $ dx + dy + where dx = (x - x') ** 2 + dy = (y - y') ** 2 + +-- Types can have multiple data constructors with arguments, too + +data Name = Mononym String | FirstLastName String String | FullName String String String + +-- To make things clearer we can use record syntax + +data Point2D = CartesianPoint2D { x :: Float, y :: Float } | PolarPoint2D { r :: Float, theta :: Float } + +myPoint = CartesianPoint2D { x = 7.0, y = 10.0 } + +-- Using record syntax automatically creates accessor functions (the name of the field) + +xOfMyPoint = x myPoint + +-- xOfMyPoint is equal to 7.0 + +-- Record syntax also allows a simple form of update + +myPoint' = myPoint { x = 9.0 } + +-- myPoint' is CartesianPoint2D { x = 9.0, y = 10.0 } + +-- Even if a type is defined with record syntax, it can be declared like +-- a simple data constructor. This is fine: + +myPoint'2 = CartesianPoint2D 3.3 4.0 + +-- It's also useful to pattern match data constructors in `case` expressions + +distanceFromOrigin x = case x of (CartesianPoint2D x y) -> sqrt $ x ** 2 + y ** 2 + (PolarPoint2D r _) -> r + +-- Your data types can have type parameters too: data Maybe a = Nothing | Just a @@ -313,8 +369,93 @@ Just "hello" -- of type `Maybe String` Just 1 -- of type `Maybe Int` Nothing -- of type `Maybe a` for any `a` +-- For convenience we can also create type synonyms with the 'type' keyword + +type String = [Char] + +-- Unlike `data` types, type synonyms need no constructor, and can be used +-- anywhere a synonymous data type could be used. Say we have the +-- following type synonyms and items with the following type signatures + +type Weight = Float +type Height = Float +type Point = (Float, Float) +getMyHeightAndWeight :: Person -> (Height, Weight) +findCenter :: Circle -> Point +somePerson :: Person +someCircle :: Circle +distance :: Point -> Point -> Float + +-- The following would compile and run without issue, even though it does not make +-- sense semantically, because the type synonyms reduce to the same base types + +distance (getMyHeightAndWeight somePerson) (findCenter someCircle) + +---------------------------------------------------- +-- 8. Typeclasses +---------------------------------------------------- + +-- Typeclasses are one way Haskell does polymorphism +-- They are similar to interfaces in other languages +-- A typeclass defines a set of functions that must work on any type that is in +-- that typeclass. + +-- The Eq typeclass is for types whose instances can be tested for equality with one another + +class Eq a where + (==) :: a -> a -> Bool + (/=) :: a -> a -> Bool + x == y = not (x /= y) + x /= y = not (x == y) + +-- This defines a typeclass that requires two functions, (==) and (/=) +-- It also declares that one function can be declared in terms of another +-- So it is enough that *either* the (==) function or the (/=) is defined +-- And the other will be 'filled in' based on the typeclass definition + +-- To make a type a member of a type class, the instance keyword is used + +instance Eq TrafficLight where + Red == Red = True + Green == Green = True + Yellow == Yellow = True + _ == _ = False + +-- Now we can use (==) and (/=) with TrafficLight objects + +canProceedThrough :: TrafficLight -> Bool +canProceedThrough t = t /= Red + +-- You can NOT create an instance definition for a type synonym + +-- Functions can be written to take typeclasses with type parameters, rather than types, +-- assuming that the function only relies on features of the typeclass + +isEqual (Eq a) => a -> a -> Bool +isEqual x y = x == y + +-- Note that x and y MUST be the same type, as they are both defined as being of type parameter 'a' +-- A typeclass does state that different types in the typeclass can be mixed together +-- So `isEqual Red 2` is invalid, even though 2 is an Int which is an instance of Eq, and Red is +-- a TrafficLight which is also an instance of Eq + +-- Other common typeclasses are: +-- Ord for types that can be ordered, allowing you to use >, <=, etc. +-- Read for types that can be created from a string representation +-- Show for types that can be converted to a string for display +-- Num, Real, Integral, Fractional for types that can do mathematical calculation +-- Enum for types that can be stepped through +-- Bounded for types with a maximum and minimum + +-- Haskell can automatically make types part of Eq, Ord, Read, Show, Enum, and Bounded +-- with the `deriving` keyword at the end of the type declaration + +data Point = Point Float Float deriving (Eq, Read, Show) + +-- In this case it is NOT necessary to create an 'instance' definition + ---------------------------------------------------- --- 8. Haskell IO +-- 9. Haskell IO ---------------------------------------------------- -- While IO can't be explained fully without explaining monads, @@ -395,7 +536,7 @@ main'' = do ---------------------------------------------------- --- 9. The Haskell REPL +-- 10. The Haskell REPL ---------------------------------------------------- -- Start the repl by typing `ghci`. -- cgit v1.2.3 From 35fc26b754a8696e505024c5b364d54e7adcde60 Mon Sep 17 00:00:00 2001 From: David Sampson Date: Mon, 4 Nov 2019 11:05:21 -0600 Subject: fixed line lengths --- haskell.html.markdown | 48 +++++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 19 deletions(-) (limited to 'haskell.html.markdown') diff --git a/haskell.html.markdown b/haskell.html.markdown index f3b84bdd..1cc79ec9 100644 --- a/haskell.html.markdown +++ b/haskell.html.markdown @@ -330,15 +330,19 @@ distance (Point x y) (Point x' y') = sqrt $ dx + dy -- Types can have multiple data constructors with arguments, too -data Name = Mononym String | FirstLastName String String | FullName String String String +data Name = Mononym String + | FirstLastName String String + | FullName String String String -- To make things clearer we can use record syntax -data Point2D = CartesianPoint2D { x :: Float, y :: Float } | PolarPoint2D { r :: Float, theta :: Float } +data Point2D = CartesianPoint2D { x :: Float, y :: Float } + | PolarPoint2D { r :: Float, theta :: Float } myPoint = CartesianPoint2D { x = 7.0, y = 10.0 } --- Using record syntax automatically creates accessor functions (the name of the field) +-- Using record syntax automatically creates accessor functions +-- (the name of the field) xOfMyPoint = x myPoint @@ -357,8 +361,9 @@ myPoint'2 = CartesianPoint2D 3.3 4.0 -- It's also useful to pattern match data constructors in `case` expressions -distanceFromOrigin x = case x of (CartesianPoint2D x y) -> sqrt $ x ** 2 + y ** 2 - (PolarPoint2D r _) -> r +distanceFromOrigin x = + case x of (CartesianPoint2D x y) -> sqrt $ x ** 2 + y ** 2 + (PolarPoint2D r _) -> r -- Your data types can have type parameters too: @@ -386,8 +391,9 @@ somePerson :: Person someCircle :: Circle distance :: Point -> Point -> Float --- The following would compile and run without issue, even though it does not make --- sense semantically, because the type synonyms reduce to the same base types +-- The following would compile and run without issue, +-- even though it does not make sense semantically, +-- because the type synonyms reduce to the same base types distance (getMyHeightAndWeight somePerson) (findCenter someCircle) @@ -397,10 +403,11 @@ distance (getMyHeightAndWeight somePerson) (findCenter someCircle) -- Typeclasses are one way Haskell does polymorphism -- They are similar to interfaces in other languages --- A typeclass defines a set of functions that must work on any type that is in --- that typeclass. +-- A typeclass defines a set of functions that must +-- work on any type that is in that typeclass. --- The Eq typeclass is for types whose instances can be tested for equality with one another +-- The Eq typeclass is for types whose instances can +-- be tested for equality with one another. class Eq a where (==) :: a -> a -> Bool @@ -428,27 +435,30 @@ canProceedThrough t = t /= Red -- You can NOT create an instance definition for a type synonym --- Functions can be written to take typeclasses with type parameters, rather than types, --- assuming that the function only relies on features of the typeclass +-- Functions can be written to take typeclasses with type parameters, +-- rather than types, assuming that the function only relies on +-- features of the typeclass isEqual (Eq a) => a -> a -> Bool isEqual x y = x == y --- Note that x and y MUST be the same type, as they are both defined as being of type parameter 'a' --- A typeclass does state that different types in the typeclass can be mixed together --- So `isEqual Red 2` is invalid, even though 2 is an Int which is an instance of Eq, and Red is --- a TrafficLight which is also an instance of Eq +-- Note that x and y MUST be the same type, as they are both defined +-- as being of type parameter 'a'. +-- A typeclass does not state that different types in the typeclass can +-- be mixed together. +-- So `isEqual Red 2` is invalid, even though 2 is an Int which is an +-- instance of Eq, and Red is a TrafficLight which is also an instance of Eq -- Other common typeclasses are: -- Ord for types that can be ordered, allowing you to use >, <=, etc. -- Read for types that can be created from a string representation -- Show for types that can be converted to a string for display --- Num, Real, Integral, Fractional for types that can do mathematical calculation +-- Num, Real, Integral, Fractional for types that can do math -- Enum for types that can be stepped through -- Bounded for types with a maximum and minimum --- Haskell can automatically make types part of Eq, Ord, Read, Show, Enum, and Bounded --- with the `deriving` keyword at the end of the type declaration +-- Haskell can automatically make types part of Eq, Ord, Read, Show, Enum, +-- and Bounded with the `deriving` keyword at the end of the type declaration data Point = Point Float Float deriving (Eq, Read, Show) -- cgit v1.2.3