class MyEq a where
myEqual :: a -> a -> Bool
myNotEqual :: a -> a -> Bool
MyEq
is the name of the type class (like data types, it must begin with a capital letter), a
is a type belonging to this class. A class can have several FooClass abc
( FooClass abc
), but in this case only one.a
belongs to the class MyEq
, if corresponding functions are defined for it.myEqual
and myNotEqual
can be expressed through each other:myEqual xy = not ( myNotEqual xy )
myNotEqual xy = not ( myEqual xy )
Such definitions will lead to infinite recursion, but in an instance of a class it is enough to define at least one of them.Bool
:instance MyEq Bool where
myEqual True True = True
myEqual False False = True
myEqual _ _ = False
An instance definition begins with the keyword instance
, instead of a variable of type a
in the definition of the class itself, we write the type for which the instance is defined, i.e. Bool
.myEqual
and now we can check the result in the interpreter:ghci> myEqual True True
True
ghci> myNotEqual True False
True
ghci> :t myEqual
myEqual :: (MyEq a) => a -> a -> Bool
We see that the type of the myEqual
function imposes a constraint (constraints) on the type - it must belong to the class MyEq
.class ( MyEq a ) => SomeClass a where
-- ...
myEqual
function, it takes two values ​​of type a
, whereas the virtual function takes only one hidden parameter this
.instance MyEq Bool
and an instance MyEq Int
, a call to the function myEqual True 5
will fail:ghci> myEqual True (5::Int)
<interactive>:1:14:
Couldn't match expected type `Bool' against inferred type `Int'
In the second argument of `myEqual', namely `(5::Int)'
In the expression: myEqual True (5 :: Int)
In the definition of `it': it = myEqual True (5 :: Int)
ghci>
The compiler (interpreter) knows that myEqual
parameters must be of the same type and therefore prevents such attempts.foo :: ( Eq a , Show a , Read a ) => a -> String -> String
How to do this, for example, in C ++ / C #? Create a composite IEquableShowableReadable
? But you will not inherit from it. Pass the argument three times with coercion to different interfaces and assume inside the function that it is the same object, and the responsibility lies with the caller?Show
(function show :: a -> String
)?{-#
OPTIONS_GHC
-XExistentialQuantification
#-}
-- , , TAB' GHC
Now we can declare this data type:data ShowObj = forall a . Show a => ShowObj a
(Read more about existential types )Show
, so that he himself also belonged to him:instance Show ShowObj where
show ( ShowObj x ) = show x
Check:ghci> [ShowObj 4, ShowObj True, ShowObj (ShowObj 'x')]
[4,True,'x']
Although I didn’t explicitly call the show
function, the interpreter uses it, so you can be sure that everything works.instance
is the definition of a dictionary instance for a particular type.Eq
in Haskell and in C ++:-- class MyEq
data MyEq a = MyEq {
myEqual :: a -> a -> Bool ,
myNotEqual :: a -> a -> Bool }
-- instance MyEq Bool
myEqBool = MyEq {
myEqual = \ xy -> x == y ,
myNotEqual = \ xy -> not ( x == y ) }
-- foo :: (MyEq a) => a -> a -> Bool
foo :: MyEq a -> a -> a -> Bool
foo dict xy = ( myEqual dict ) xy
-- foo True False
fooResult = foo myEqBool True False
The same on C ++:#include <iostream> <br/>
<br/>
// class MyEq a <br/>
class MyEq<br/>
{ <br/>
public : <br/>
virtual ~MyEq ( ) throw ( ) { } <br/>
// void const *, <br/>
virtual bool unsafeMyEqual ( void const * x, void const * y ) const = 0 ;<br/>
virtual bool unsafeMyNotEqual ( void const * x, void const * y ) const = 0 ;<br/>
} ;<br/>
<br/>
// , <br/>
// <br/>
template < typename T > <br/>
class MyEqDictBase : public MyEq<br/>
{ <br/>
virtual bool unsafeMyEqual ( void const * x, void const * y ) const <br/>
{ return myEqual ( * static_cast < T const * > ( x ) , * static_cast < T const * > ( y ) ) ; } <br/>
virtual bool unsafeMyNotEqual ( void const * x, void const * y ) const <br/>
{ return myNotEqual ( * static_cast < T const * > ( x ) , * static_cast < T const * > ( y ) ) ; } <br/>
public : <br/>
virtual bool myEqual ( T const & x, T const & y ) const { return ! myNotEqual ( x, y ) ; } <br/>
virtual bool myNotEqual ( T const & x, T const & y ) const { return ! myEqual ( x, y ) ; } <br/>
} ;<br/>
<br/>
// . . <br/>
template < typename T > <br/>
class MyEqDict;<br/>
<br/>
// <br/>
template < typename T > <br/>
MyEqDict < T > makeMyEqDict ( ) { return MyEqDict < T > ( ) ; } <br/>
<br/>
// instance MyEq Bool <br/>
// MyEq bool <br/>
template <> <br/>
class MyEqDict < bool > : public MyEqDictBase < bool > <br/>
{ <br/>
public : <br/>
virtual bool myEqual ( bool const & l, bool const & r ) const { return l == r; } <br/>
} ;<br/>
<br/>
// <br/>
// , bool, Haskell' <br/>
bool fooDict ( MyEq const & dict, void const * x, void const * y ) <br/>
{ <br/>
return dict. unsafeMyNotEqual ( x, y ) ; // myNotEqual <br/>
} <br/>
<br/>
// <br/>
// foo :: (MyEq a) => a -> a -> Bool <br/>
template < typename T > <br/>
bool foo ( T const & x, T const & y ) <br/>
{ <br/>
return fooDict ( makeMyEqDict < T > ( ) , & x, & y ) ;<br/>
} <br/>
<br/>
int main ( ) <br/>
{ <br/>
std :: cout << foo ( true , false ) << std :: endl ; // 1 <br/>
std :: cout << foo ( false , false ) << std :: endl ; // 0 <br/>
return 0 ;<br/>
}
[ 1 .. 10 ]
, in fact, this means enumFromTo 1 10
, [ 1 , 3 .. 10 ] => enumFromThenTo 1 3 10
show :: a -> String
.read :: String -> a
.Maybe a
(optional value), to signal an error in a “clean” way, rather than a “dirty” exception.( == )
and ( /= )
(as if crossed out equality sign)( < )
( > )
( <= )
( >= )
. Requires type belonging to class Eq .fmap :: ( a -> b ) -> fa -> fb
.ghci> fmap (+1) [1,2,3]
[2,3,4]
ghci> fmap (+1) (Just 6)
Just 7
ghci> fmap (+1) Nothing
Nothing
ghci> fmap reverse getLine
hello
"olleh"
Those. for the list, this is just a map
, for the optional Maybe a
value, the function ( + 1 )
is used if the value is present, and for I / O the function is applied to the result of this I / O.getLine
does not return a string, so you can’t directly apply reverse
to it.data Test a = NoValue | Test aa deriving ( Show , Read , Eq , Ord )
data Color = Red | Green | Yellow | Blue | Black | Write deriving ( Show , Read , Enum , Eq , Ord )
ghci> NoValue == ( Test 4 5 )
False
ghci> read "Test 5 6" :: Test Int
Test 5 6
ghci> ( Test 1 100 ) < ( Test 2 0 )
True
ghci> ( Test 2 100 ) < ( Test 2 0 )
False
ghci> [ Red .. Black ]
[ Red , Green , Yellow , Blue , Black ]
class Computation c where
return :: a -> ca
bind :: ca -> ( a -> cb ) -> cb
The first function essentially takes some value and returns a calculation that, when executed, will simply return the same value.a
a
and returns a new calculation that returns a value of type b
b
.data Maybe a = Just a | Nothing
Suppose we have several functions, each of which can fail and return Nothing
. Then, using them directly, we risk getting such a weak code:Those. instead of simply calling these functions in sequence, you have to check the value each time.funOnMaybes x =
case functionMayFail1 x of
Nothing -> Nothing -- ,
Just x1 -> -- , ,
case functionMayFail2 x1 of
Nothing -> Nothing -- ( "")
Just x2 -> --
combineMaybe :: Maybe a -> ( a -> Maybe b ) -> Maybe b
combineMaybe Nothing _ = Nothing
combineMaybe ( Just x ) f = fx
The combineMaybe
function accepts an optional value and function, and returns the result of applying this function to an optional value, or failure.funOnMaybes
function:Or, using the fact that the function can be called infix:funOnMaybes x = combineMaybe ( combineMaybe x functionMayFail1 ) functionMayFail2 --...
funOnMaybes x = x `combineMaybe` functionMayFail1 `combineMaybe` functionMayFail2 --...
combineMaybe
function exactly repeats the type of bind
, only Maybe
stands instead of c
.instance Computation Maybe where
return x = Just x
bind Nothing f = Nothing
bind ( Just x ) f = fx
This is exactly what the Maybe
monad is defined to, only bind
is called there ( >>= )
, plus there is an additional operator ( >> )
that does not use the result of the previous computation.funOnMaybes x = do
x1 <- functionMayFail1 x
x2 <- functionMayFail2 x1
if x2 == (0)
then return (0)
else do
x3 <- functionMayFail3 x2
return ( x1 + x3 )
Pay attention to the extra do
inside and then
. do
is just syntactic sugar, which combines several calculations into one, and since in the then
branch there is a calculation and so is one ( return (0) )
, then do
is not needed there; in the else
branch, the calculations are two in a row, and in order to combine them, you need to use do
again.(<-)
converted very simply:( >> )
let ... in
( >>= )
p
, which may not coincide.fail
function is another additional function in the monad, which generally has no relation to the concept of monads.get/put
functions that are aware of the internal structure of the State
, it is possible to get and set state.import Control . Monad . State
fact' :: Int -> State Int Int -- - Int, - Int
fact' 0 = do
acc <- get --
return acc --
fact' n = do
acc <- get --
put ( acc * n ) -- n
fact' ( n - 1 ) --
fact :: Int -> Int
fact n = fst $ runState ( fact' n ) 1 -- - 1
runState
evaluates a function with a state, returns a tuple with a result and an altered state. We only need the result, so fst
.dummyFun contents = do
l <- lines contents --
if length l < 3 then fail "" -- 3
else do
w <- words l --
return w --
ghci> dummyFun "line1\nword1 word2 word3\n \n \nline5"
["line1","word1","word2","word3","line5"]
getLine
function getLine
:getLine :: IO String
An IO action that returns a string to us. IO
can be understood as:getLine :: World -> ( String , World )
where World
is the state of the worldIO
, we will never be able to parse the result of type IO String
into its components, but will only have to use it in other calculations, thus ensuring that the use of I / O will be reflected in type of function.unsafePerformIO :: IO a -> a
, but that's unsafe, to be used only with an understanding of the matter and when it is absolutely necessary. I personally have never used it).State
, if I / O is IO
. But what if our function wants to have states and still perform input and output?State
and another monad. To perform calculations in this other monad, use the lift
function.import Control . Monad . State
fact' :: Int -> StateT Int IO Int -- IO
fact' 0 = do
acc <- get
return acc
fact' n = do
acc <- get
lift $ print acc -- print acc - IO (), lift
put ( acc * n )
fact' ( n - 1 )
fact :: Int -> IO Int -- IO , :)
fact n = do
( r , _ ) <- runStateT ( fact' n ) 1
return r
ghci> fact 5
1
5
20
60
120
120
sequence :: Monad m => [ ma ] -> m [ a ]
--
mapM f = sequence . map f
forM = flip mapM
-- forM [1..10] $ \i -> print i
forever :: Monad m => ma -> mb -- ,
filterM :: Monad m => ( a -> m Bool ) -> [ a ] -> m [ a ] -- filter
foldM :: Monad m => ( a -> b -> ma ) -> a -> [ b ] -> ma -- foldl
when :: Monad m => Bool -> m () -> m () -- if else
ghci> take 5 $ [ 2 * x | x <- [ 1 .. ] , x ^ 2 > 3 ]
[ 4 , 6 , 8 , 10 , 12 ]
ghci> [ ( x , y ) | x <- [ 1 .. 3 ] , y <- [ 1 .. 3 ] ]
[ ( 1 , 1 ) , ( 1 , 2 ) , ( 1 , 3 ) , ( 2 , 1 ) , ( 2 , 2 ) , ( 2 , 3 ) , ( 3 , 1 ) , ( 3 , 2 ) , ( 3 , 3 ) ]
It is seen that the last list is moved more often.ghci> [ x + y | x <- [ 1 .. 4 ] , y <- [ 1 .. x ] , even x ]
[ 3 , 4 , 5 , 6 , 7 , 8 ]
do
x <- [ 1 .. 4 ]
y <- [ 1 .. x ]
True <- return ( even x )
return ( x + y )
Of course, it unfolds directly, but so is the connection between monadic calculations on lists and list comprehensions.Source: https://habr.com/ru/post/51046/
All Articles