class MyEq a where
myEqual :: a -> a -> Bool
myNotEqual :: a -> a -> BoolMyEq 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 _ _ = FalseAn 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 -> BoolWe 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 -> StringHow 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' GHCNow 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 xCheck: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 FalseThe 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 10show :: 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 ) -> cbThe first function essentially takes some value and returns a calculation that, when executed, will simply return the same value.aa and returns a new calculation that returns a value of type bb .data Maybe a = Just a | NothingSuppose 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 = fxThe 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 = fxThis 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 StringAn 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
120sequence :: 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