mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2025-04-27 15:43:58 +00:00
Info on typeclasses and types
This commit is contained in:
parent
f9d74a3191
commit
f276866777
@ -293,7 +293,13 @@ foldr (\x y -> 2*x + y) 4 [1,2,3] -- 16
|
|||||||
-- 7. Data Types
|
-- 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
|
data Color = Red | Blue | Green
|
||||||
|
|
||||||
@ -304,7 +310,57 @@ say Red = "You are Red!"
|
|||||||
say Blue = "You are Blue!"
|
say Blue = "You are Blue!"
|
||||||
say Green = "You are Green!"
|
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
|
data Maybe a = Nothing | Just a
|
||||||
|
|
||||||
@ -313,8 +369,93 @@ Just "hello" -- of type `Maybe String`
|
|||||||
Just 1 -- of type `Maybe Int`
|
Just 1 -- of type `Maybe Int`
|
||||||
Nothing -- of type `Maybe a` for any `a`
|
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. Haskell IO
|
-- 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
|
||||||
|
|
||||||
|
----------------------------------------------------
|
||||||
|
-- 9. Haskell IO
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
-- While IO can't be explained fully without explaining monads,
|
-- 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`.
|
-- Start the repl by typing `ghci`.
|
||||||
|
Loading…
Reference in New Issue
Block a user