mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2024-12-29 10:12:19 +03:00
commit
9eac54a1d4
@ -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,62 @@ 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 +374,98 @@ 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. 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 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 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
|
||||
|
||||
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,
|
||||
@ -395,7 +546,7 @@ main'' = do
|
||||
|
||||
|
||||
----------------------------------------------------
|
||||
-- 9. The Haskell REPL
|
||||
-- 10. The Haskell REPL
|
||||
----------------------------------------------------
|
||||
|
||||
-- Start the repl by typing `ghci`.
|
||||
|
Loading…
Reference in New Issue
Block a user