# Write Yourself a Scheme in 48 Hours ## 1. First Steps ```haskell module Main where import System.Environment main :: IO () main = do args <- getArgs putStrLn $ "Hello," ++ args !! 0 ``` - `()` unit type - 这种类型只有一个值就是`()` - `IO`类型是`Monad`的一个实例 - 创建一个`IO Action` - `Lift`一个普通值到`IO monad`中 - 组合两个已有的`IO Action`,形成一个新的 - `do block` - 不能在同一个do-block中混用不同的monad类型 - `Monad`是一个concept - 有额外信息贴在这个值上 - `Monadic value` - `IO ()`中的`()`就是 - 大部分函数不用关注这些额外信息 exercises: ```haskell main :: IO () main = do args <- getArgs putStrLn $ "Hello," ++ args !! 0 ++ "," ++ args !! 1 putStrLn $ show $ (read (args !! 0)) * (read (args !! 1)) line <- getLine putStrLn $ "Hello," ++ line ``` ## 2. Parsing ### 2.1 Writing a Simple Parser ```haskell import Text.ParserCombinators.Parsec import System.Environment symbol :: Parser Char symbol = oneOf "!#$%&|*+-/:<=>?@^_~" _spaces :: Parser () _spaces = skipMany1 space readExpr :: String -> String readExpr input = case parse (_spaces >> symbol) "lisp" input of Left err -> "No match:" ++ show err Right val -> "Found value:" ++ show val main :: IO () main = do (expr:_) <- getArgs putStrLn $ readExpr expr ``` - `Parsec` - `cabal install parsec` - `Parser` - `symbol`定义 - 一个monad,其中额外信息有 - input stream中的位置 - backtracking记录 - fisrt&follow集 - `parse` - `parse :: Stream s Identity t => Parsec s () a -> SourceName -> s -> Either ParseError a` - `Either`类型 - `data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)` - `case ... of` 模式匹配 ### 2.2 Whitespace ```haskell _spaces :: Parser () _spaces = skipMany1 space readExpr :: String -> String readExpr input = case parse (_spaces >> symbol) "lisp" input of Left err -> "No match:" ++ show err Right val -> "Found value:" ++ show val ``` 增加对空字符的识别跳过. - `>>` bind(不带值) - Attempt to match the first parser, then attempt to match the second with the remaining input, and fail if either fails. ### 2.3 Return values ```haskell data LispVal = Atom String | List [LispVal] | DottedList [LispVal] LispVal | Number Integer | String String | Bool Bool parseString :: Parser LispVal parseString = do char '"' x <- many (noneOf "\"") char '"' return $ String x parseAtom :: Parser LispVal parseAtom = do first <- letter <|> symbol rest <- many (letter <|> digit <|> symbol) let atom = first: rest return $ case atom of "#t" -> Bool True "#f" -> Bool False _ -> Atom atom parseNumber :: Parser LispVal parseNumber = liftM (Number . read) $ many1 digit parseExpr :: Parser LispVal parseExpr = parseAtom <|> parseString <|> parseNumber ``` - 抽象数据类型 - `parseString` - `$` 改变结合顺序 - `return` lift前面`many`拿到的值进入到Parser monad中 - `parseAtom` - `<|>`, choice operator(一个combinator) - 尝试第一个parser,如果失败尝试下一个 - 一个parser一定是在它消费输入前失败掉, 但是它需要看输入才能判断是否失败, 因此需要backtrack. - `:`, list cons operator - `parseNumber` - 从右向左读,函数都是右结合的 - `many1 digit`返回的是`Parser String`, 没有办法只是应用`(Number . read)` - `liftM`将函数应用到monad中的值里,