Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
pp:2025:l11 [2025/05/16 20:58] cata_chiru |
pp:2025:l11 [2025/05/18 13:35] (current) cata_chiru [11.3. Nice to read] |
||
---|---|---|---|
Line 91: | Line 91: | ||
- | 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 ===== | ===== 11.2. More Applicatives ===== | ||
- | Todo exemplu similar cu baza de date | + | 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) | ||