Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
pp:2025:l11 [2025/05/16 20:50]
cata_chiru
pp:2025:l11 [2025/05/18 13:35] (current)
cata_chiru [11.3. Nice to read]
Line 34: Line 34:
 We see that the only requirement for Monad to work is overriding ''​( >>= )''​. We see that the only requirement for Monad to work is overriding ''​( >>= )''​.
  
-However, the catch comes from the restriction <code haskell> class Applicative m => Monad m ... </​code>​+However, the catch comes from the restriction<code haskell> class Applicative m => Monad m ... </​code>​
  
 By looking at the Applicative:​ By looking at the Applicative:​
Line 77: Line 77:
 3. data MyMaybe a = ... 3. data MyMaybe a = ...
  
-4. data MyEither a b = ... +4. data MyPair a b = ...
- +
-5. data MyPair a b = ...+
  
 <​note>​ <​note>​
-As you can see from :info . \\+As you can see from :info, the type for our classes is unary (type Class :: * -> Constraint),​ meaning that they expect to add just another type as parameter, as in MyList that expects and a. \\ 
 + 
 +However, MyPair needs 2 types to form an object. We can think of MyPair as a hash set (key, value) and curry the types: 
 + 
 +<code haskell> instance Class (MyPair a) where ... </​code>​ 
 + 
 +Meaning "I already got type a, I expect you to give type b and apply your operations only on the object of type b.".
  
 </​note>​ </​note>​
  
  
-Create objects for each class and test the validity of your class implementations with examples for show, fmap, <*>, >>=.+Create objects for each class and test the validity of your class implementations with examples for show, fmap, $<*>$$>>=$. 
 + 
 +===== 11.2. More Applicatives ===== 
 + 
 +Applicates, as monads, are used preponderantly for type construction and validation. 
 + 
 +5. For the first task we will want to validate a database of users. 
 + 
 +Being given the following data types: 
 + 
 +<code haskell>​ 
 + 
 +data User = User 
 +  { name     :: String 
 +  , age      :: Int 
 +  , balance :: Int 
 +  , password :: String 
 +  } deriving Show 
 + 
 +data Validation e a = Valid a | Invalid e deriving Show 
 + 
 +</​code>​ 
 + 
 +5.1. Create the instance of Applicative for Validation. 
 + 
 +5.2. Create functions to validate each attribute of a user. 
 + 
 +<code haskell>​ 
 +validateAttribute :: a -> Validation [String] a 
 +</​code>​ 
 + 
 +For example, validateAge will only allow Users with positive ages under < 150 years, otherwise it will return an Error. 
 + 
 +For validateName,​ we want to make sure name starts with a capital letter and only contains letters. 
 + 
 +Password has to have at least 6 characters, a majuscule and a special character. 
 + 
 +Balance can also be negative. 
 + 
 +5.3. Make use of the above functions, and the functions from Applicative,​ as well as infixed fmap ''​(<​$>​)''​ to create a validation function for the user. 
 + 
 +<code haskell>​ 
 +validateUser :: String -> Int -> Int -> String -> Validation [String] User 
 +</​code>​ 
 + 
 +5.4 Create unpackUser that takes a tuple and returns a User: 
 + 
 +<code haskell>​ 
 +unpackUser :: (String, Int, Int, String) -> User 
 +</​code>​ 
 + 
 +5.5 Being given the following list of user data: 
 + 
 +<code haskell>​ 
 +user_data = [("​Catalin",​ 25, -1000, "​aBc123!"​),​ ("​Nicoleta",​ 19, 1000, "​da"​),​ ("​Nicoleta",​ 19, 1000, "​Daba12_"​),​ ("​Andrei",​ 30, 2000, "​pAr.la2"​),​ ("​Iust1n",​ 45, 3000, "​kajD413!"​),​ ("​Iustin",​ 45, 3000, "​kajD413!"​),​ ("​Maria",​ 28, -1500, "​parOla3!"​)] 
 + 
 +type BankAccount = [Validation [String] User] 
 +</​code>​ 
 + 
 +Create a function that filters the invalid users and adds them to a database: 
 + 
 +<code haskell>​ 
 +addUsers :: BankAccount -> [User] -> BankAccount 
 +</​code>​ 
 + 
 +5.6 Filter the users with a negative balance. 
 + 
 +<code haskell>​ 
 +filter_debtors :: BankAccount -> BankAccount 
 +</​code>​ 
 + 
 +6. Recall what a parser is from the course lecture: 
 + 
 +<code haskell>​ 
 +import Data.Char 
 +import Control.Applicative 
 + 
 +data Expr = Var String | Val Int | Plus Expr Expr deriving Show 
 + 
 +data Parser a = Parser (String -> [(a,​String)]) 
 + 
 +parse (Parser p) s = p s 
 + 
 +instance Monad Parser where 
 +--    (>>=) :: Parser a -> (a -> Parser b) -> Parser b  
 +    mp >>= f = Parser $ \s -> 
 +                case parse mp s of 
 +                     [] -> [] 
 +                     ​[(v,​r)] -> parse (f v) r 
 +    return x = Parser $ \s -> [(x,s)] 
 + 
 +instance Applicative Parser where 
 +    af <*> mp =  
 +        do  
 +            f <- af 
 +            v <- mp 
 +            return $ f v 
 +    pure = return 
 + 
 +instance Functor Parser where  
 +    fmap  f mp =  
 +        do  
 +            x <- mp 
 +            return $ f x 
 + 
 + 
 +charParser :: Char -> Parser Char 
 +charParser c = Parser $ \s ->  
 +                case s of  
 +                    [] -> [] 
 +                    (x:xs) -> if (x == c) then [(x,xs)] else [] 
 + 
 +predicateParser :: (Char -> Bool) -> Parser Char 
 +predicateParser p = Parser $ \s -> 
 +                        case s of 
 +                            [] -> [] 
 +                            (x:xs) -> if p x then [(x,xs)] else [] 
 + 
 + 
 + 
 +</​code>​ 
 + 
 +6.1 Try to understand and reimplement the instances for Functor, Applicative and Monad for Parser yourself. 
 + 
 +6.2 Using the definitions above and functions from Data.Char [5], implement letter - that parses one letter of the input, and digit - that parses one digit (given as a char) of the input. 
 + 
 +<code haskell>​ 
 +letter :: Parser Char 
 +letter = undefined 
 + 
 +digit :: Parser Char 
 +digit = undefined 
 +</​code>​ 
 + 
 +6.3 Use letter, digit and functions from the classes studied in this laboratory to define letterDigit that parses one letter and one digit one after the other: 
 + 
 +<code haskell>​ 
 +letterDigit :: Parser (Char,​Char) 
 +letterDigit = undefined 
 + 
 +positive_parse_ex = parse letterDigit "​a1b2c3"​ -- by calling this in the command line we should get [(('​a','​1'​),"​b2c3"​)] 
 +negative_parse_ex = parse letterDigit "​aa2a"​ -- by calling this in the command line we should get [] 
 +</​code>​ 
 + 
 + 
 +===== 11.3. Nice to read ===== 
 + 
 +1. https://​mmhaskell.com/​monads/​applicatives 
 + 
 +2. https://​learnyouahaskell.com/​functors-applicative-functors-and-monoids 
 + 
 +3. https://​learnyouahaskell.com/​a-fistful-of-monads 
 + 
 +4. https://​www.youtube.com/​watch?​v=FLAPIgvlVnE&​t=1945s&​ab_channel=JamesHobson 
 + 
 +5. [Data.Char Library](https://​hackage.haskell.org/​package/​base-4.21.0.0/​docs/​Data-Char.html)