├── .gitignore ├── README.md ├── _layout.jade ├── css ├── main.styl └── prism.css ├── ex ├── _data.json ├── _layout.jade ├── arrays.md ├── atomic-counters.md ├── base64-encoding.md ├── channel-buffering.md ├── channel-directions.md ├── channel-synchronization.md ├── channels.md ├── closures.md ├── collection-functions.md ├── command-line-arguments.md ├── command-line-flags.md ├── constants.md ├── defer.md ├── environment-variables.md ├── epoch.md ├── errors.md ├── execing-processes.md ├── exit.md ├── for.md ├── functions.md ├── goroutines.md ├── hello-world.md ├── if-else.md ├── interfaces.md ├── json.md ├── line-filters.md ├── maps.md ├── methods.md ├── multiple-return-values.md ├── mutexes.md ├── non-blocking-channel-operations.md ├── number-parsing.md ├── panic.md ├── random-numbers.md ├── range.md ├── rate-limiting.md ├── reading-files.md ├── recursion.md ├── regular-expressions.md ├── select.md ├── sha1-hashes.md ├── signals.md ├── slices.md ├── sorting-by-functions.md ├── sorting.md ├── spawning-processes.md ├── stateful-goroutines.md ├── string-formatting.md ├── string-functions.md ├── structs.md ├── switch.md ├── tickers.md ├── time-formatting-parsing.md ├── time.md ├── timeouts.md ├── timers.md ├── url-parsing.md ├── values.md ├── variables.md ├── variadic-functions.md ├── worker-pools.md └── writing-files.md ├── index.jade ├── js └── prism.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Haskell By Example 2 | ### Building 3 | 4 | ```bash 5 | $ npm install 6 | $ npm start 7 | ``` 8 | 9 | 10 | 11 | ### Contribution 12 | 13 | Feel free to send a PR or create a new issue. 14 | 15 | ### License 16 | 17 | This work is copyright Tatsuya Hirose and licensed under a 18 | [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/). 19 | 20 | The Go by Example is copyright [Mark McGranaghan](https://github.com/mmcgrana) and licensed under a 21 | [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/). 22 | 23 | The Go Gopher is copyright [Renée French](http://reneefrench.blogspot.com/) and licensed under a 24 | [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/). 25 | -------------------------------------------------------------------------------- /_layout.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title Haskell by Example 5 | link(rel="stylesheet" href="css/main.css") 6 | body 7 | != yield 8 | -------------------------------------------------------------------------------- /css/main.styl: -------------------------------------------------------------------------------- 1 | html, body 2 | font-family: Georgia, serif 3 | 4 | pre, code 5 | font-size: 14px 6 | line-height: 18px 7 | font-family: 'Menlo', 'Monaco', 'Consolas', 'Lucida Console', monospace 8 | 9 | #intro 10 | width: 420px 11 | margin: 0 auto 12 | a 13 | color: black 14 | ul 15 | padding: 0 16 | list-style-type: none 17 | 18 | .example 19 | width: 900px 20 | margin: 0 auto 21 | a 22 | color: gray 23 | 24 | h1, h2, h3, h4, h5, h6, h7 25 | font-weight: normal 26 | -------------------------------------------------------------------------------- /css/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+clike+bash+haskell */ 2 | /** 3 | * prism.js default theme for JavaScript, CSS and HTML 4 | * Based on dabblet (http://dabblet.com) 5 | * @author Lea Verou 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | text-shadow: 0 1px white; 12 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 13 | direction: ltr; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | line-height: 1.5; 19 | 20 | -moz-tab-size: 4; 21 | -o-tab-size: 4; 22 | tab-size: 4; 23 | 24 | -webkit-hyphens: none; 25 | -moz-hyphens: none; 26 | -ms-hyphens: none; 27 | hyphens: none; 28 | } 29 | 30 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 31 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 32 | text-shadow: none; 33 | background: #b3d4fc; 34 | } 35 | 36 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 37 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 38 | text-shadow: none; 39 | background: #b3d4fc; 40 | } 41 | 42 | @media print { 43 | code[class*="language-"], 44 | pre[class*="language-"] { 45 | text-shadow: none; 46 | } 47 | } 48 | 49 | /* Code blocks */ 50 | pre[class*="language-"] { 51 | padding: 1em; 52 | margin: .5em 0; 53 | overflow: auto; 54 | } 55 | 56 | :not(pre) > code[class*="language-"], 57 | pre[class*="language-"] { 58 | background: #f5f2f0; 59 | } 60 | 61 | /* Inline code */ 62 | :not(pre) > code[class*="language-"] { 63 | padding: .1em; 64 | border-radius: .3em; 65 | } 66 | 67 | .token.comment, 68 | .token.prolog, 69 | .token.doctype, 70 | .token.cdata { 71 | color: slategray; 72 | } 73 | 74 | .token.punctuation { 75 | color: #999; 76 | } 77 | 78 | .namespace { 79 | opacity: .7; 80 | } 81 | 82 | .token.property, 83 | .token.tag, 84 | .token.boolean, 85 | .token.number, 86 | .token.constant, 87 | .token.symbol, 88 | .token.deleted { 89 | color: #905; 90 | } 91 | 92 | .token.selector, 93 | .token.attr-name, 94 | .token.string, 95 | .token.char, 96 | .token.builtin, 97 | .token.inserted { 98 | color: #690; 99 | } 100 | 101 | .token.operator, 102 | .token.entity, 103 | .token.url, 104 | .language-css .token.string, 105 | .style .token.string { 106 | color: #a67f59; 107 | background: hsla(0, 0%, 100%, .5); 108 | } 109 | 110 | .token.atrule, 111 | .token.attr-value, 112 | .token.keyword { 113 | color: #07a; 114 | } 115 | 116 | .token.function { 117 | color: #DD4A68; 118 | } 119 | 120 | .token.regex, 121 | .token.important, 122 | .token.variable { 123 | color: #e90; 124 | } 125 | 126 | .token.important, 127 | .token.bold { 128 | font-weight: bold; 129 | } 130 | .token.italic { 131 | font-style: italic; 132 | } 133 | 134 | .token.entity { 135 | cursor: help; 136 | } 137 | 138 | -------------------------------------------------------------------------------- /ex/_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "hello-world": { 3 | "title": "Hello World" 4 | }, 5 | "values": { 6 | "title": "Values" 7 | }, 8 | "variables": { 9 | "title": "Variables" 10 | }, 11 | "constants": { 12 | "title": "Constants" 13 | }, 14 | "for": { 15 | "title": "For" 16 | }, 17 | "if-else": { 18 | "title": "If/Else" 19 | }, 20 | "switch": { 21 | "title": "Switch" 22 | }, 23 | "arrays": { 24 | "title": "Arrays" 25 | }, 26 | "slices": { 27 | "title": "Slices" 28 | }, 29 | "maps": { 30 | "title": "Maps" 31 | }, 32 | "range": { 33 | "title": "Range" 34 | }, 35 | "functions": { 36 | "title": "Functions" 37 | }, 38 | "multiple-return-values": { 39 | "title": "Multiple Return Values" 40 | }, 41 | "variadic-functions": { 42 | "title": "Variadic Functions" 43 | }, 44 | "closures": { 45 | "title": "Closures" 46 | }, 47 | "recursion": { 48 | "title": "Recursion" 49 | }, 50 | "structs": { 51 | "title": "Structs" 52 | }, 53 | "methods": { 54 | "title": "Methods" 55 | }, 56 | "interfaces": { 57 | "title": "Interfaces" 58 | }, 59 | "errors": { 60 | "title": "Errors" 61 | }, 62 | "goroutines": { 63 | "title": "Goroutines" 64 | }, 65 | "channels": { 66 | "title": "Channels" 67 | }, 68 | "channel-buffering": { 69 | "title": "Channel Buffering" 70 | }, 71 | "channel-synchronization": { 72 | "title": "Channel Synchronization" 73 | }, 74 | "channel-directions": { 75 | "title": "Channel Directions" 76 | }, 77 | "select": { 78 | "title": "Select" 79 | }, 80 | "timeouts": { 81 | "title": "Timeouts" 82 | }, 83 | "non-blocking-channel-operations": { 84 | "title": "Non-Blocking Channel Operations" 85 | }, 86 | "timers": { 87 | "title": "Timers" 88 | }, 89 | "tickers": { 90 | "title": "Tickers" 91 | }, 92 | "worker-pools": { 93 | "title": "Worker Pools" 94 | }, 95 | "rate-limiting": { 96 | "title": "Rate Limiting" 97 | }, 98 | "atomic-counters": { 99 | "title": "Atomic Counters" 100 | }, 101 | "mutexes": { 102 | "title": "Mutexes" 103 | }, 104 | "stateful-goroutines": { 105 | "title": "Stateful Goroutines" 106 | }, 107 | "sorting": { 108 | "title": "Sorting" 109 | }, 110 | "sorting-by-functions": { 111 | "title": "Sorting by Functions" 112 | }, 113 | "panic": { 114 | "title": "Panic" 115 | }, 116 | "defer": { 117 | "title": "Defer" 118 | }, 119 | "collection-functions": { 120 | "title": "Collection Functions" 121 | }, 122 | "string-functions": { 123 | "title": "String Functions" 124 | }, 125 | "string-formatting": { 126 | "title": "String Formatting" 127 | }, 128 | "regular-expressions": { 129 | "title": "Regular Expressions" 130 | }, 131 | "json": { 132 | "title": "JSON" 133 | }, 134 | "time": { 135 | "title": "Time" 136 | }, 137 | "epoch": { 138 | "title": "Epoch" 139 | }, 140 | "time-formatting-parsing": { 141 | "title": "Time Formatting / Parsing" 142 | }, 143 | "random-numbers": { 144 | "title": "Random Numbers" 145 | }, 146 | "number-parsing": { 147 | "title": "Number Parsing" 148 | }, 149 | "url-parsing": { 150 | "title": "URL Parsing" 151 | }, 152 | "sha1-hashes": { 153 | "title": "SHA1 Hashes" 154 | }, 155 | "base64-encoding": { 156 | "title": "Base64 Encoding" 157 | }, 158 | "reading-files": { 159 | "title": "Reading Files" 160 | }, 161 | "writing-files": { 162 | "title": "Writing Files" 163 | }, 164 | "line-filters": { 165 | "title": "Line Filters" 166 | }, 167 | "command-line-arguments": { 168 | "title": "Command-Line Arguments" 169 | }, 170 | "command-line-flags": { 171 | "title": "Command-Line Flags" 172 | }, 173 | "environment-variables": { 174 | "title": "Environment Variables" 175 | }, 176 | "spawning-processes": { 177 | "title": "Spawning Processes" 178 | }, 179 | "execing-processes": { 180 | "title": "Exec'ing Processes" 181 | }, 182 | "signals": { 183 | "title": "Signals" 184 | }, 185 | "exit": { 186 | "title": "Exit" 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /ex/_layout.jade: -------------------------------------------------------------------------------- 1 | doctype 2 | html 3 | head 4 | title Haskell by Example: #{title} 5 | link(rel="stylesheet" href="../css/main.css") 6 | link(rel="stylesheet" href="../css/prism.css") 7 | script(src="../js/prism.js") 8 | body 9 | .example 10 | h1 Haskell by Example: #{title} 11 | a(href="https://gobyexample.com/#{current.source}") original 12 | != yield 13 | a(href="../") back to index 14 | -------------------------------------------------------------------------------- /ex/arrays.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.Array 3 | 4 | main = do 5 | let a = array (0, 4) [(i, 0) | i <- [0..4]] 6 | putStrLn $ "emp: " ++ show a 7 | 8 | let a' = a // [(4,100)] 9 | putStrLn $ "set: " ++ show a' 10 | putStrLn $ "get: " ++ show (a' ! 4) 11 | putStrLn $ "len: " ++ show ((+1) . snd . bounds $ a') 12 | 13 | let b = array (0, 4) [(i, i+1) | i <- [0..4]] 14 | putStrLn $ "dcl: " ++ show b 15 | 16 | let twoD = array ((0,0), (1, 2)) [((i, j), i + j) | i <- [0..1], j <- [0..2]] 17 | putStrLn $ "2d: " ++ show twoD 18 | ``` 19 | 20 | ```bash 21 | $ runhaskell arrays.hs 22 | emp: array (0,4) [(0,0),(1,0),(2,0),(3,0),(4,0)] 23 | set: array (0,4) [(0,0),(1,0),(2,0),(3,0),(4,100)] 24 | get: 100 25 | len: 5 26 | dcl: array (0,4) [(0,1),(1,2),(2,3),(3,4),(4,5)] 27 | 2d: array ((0,0),(1,2)) [((0,0),0),((0,1),1),((0,2),2),((1,0),1),((1,1),2),((1,2),3)] 28 | ``` 29 | -------------------------------------------------------------------------------- /ex/atomic-counters.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Control.Monad 3 | import Control.Concurrent 4 | import Control.Concurrent.STM 5 | 6 | main = do 7 | ops <- atomically $ newTVar 0 8 | forM_ [0..49] $ \_ -> do 9 | forkIO . forever $ do 10 | atomically $ modifyTVar ops (+1) 11 | threadDelay 100 12 | 13 | threadDelay 1000000 14 | opsFinal <- atomically $ readTVar ops 15 | putStrLn $ "ops: " ++ show opsFinal 16 | ``` 17 | 18 | ```bash 19 | $ runhaskell atomic-counters.hs 20 | ops: 148407 21 | ``` 22 | -------------------------------------------------------------------------------- /ex/base64-encoding.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | {-# LANGUAGE OverloadedStrings #-} 3 | import Data.ByteString.Base64 4 | 5 | main = do 6 | let dat = "abc123!?$*&()'-=@~" 7 | let sEnc = encode dat 8 | print sEnc 9 | 10 | let sDec = decode sEnc 11 | print sDec 12 | ``` 13 | 14 | ```bash 15 | $ runhaskell base64-encoding.hs 16 | "YWJjMTIzIT8kKiYoKSctPUB+" 17 | Right "abc123!?$*&()'-=@~" 18 | ``` 19 | -------------------------------------------------------------------------------- /ex/channel-buffering.md: -------------------------------------------------------------------------------- 1 | You can treat [TQueue](http://hackage.haskell.org/package/stm/docs/Control-Concurrent-STM-TQueue.html) as an unbounded FIFO channel. 2 | 3 | ```haskell 4 | import Control.Concurrent.STM 5 | 6 | main = do 7 | messages <- atomically $ do 8 | msg <- newTQueue 9 | writeTQueue msg "buffered" 10 | writeTQueue msg "queue" 11 | return msg 12 | 13 | putStrLn =<< (atomically . readTQueue) messages 14 | putStrLn =<< (atomically . readTQueue) messages 15 | ``` 16 | 17 | ```bash 18 | $ runhaskell channel-buffering.hs 19 | buffered 20 | queue 21 | ``` 22 | -------------------------------------------------------------------------------- /ex/channel-directions.md: -------------------------------------------------------------------------------- 1 | see also [privileged-concurrency](http://hackage.haskell.org/package/privileged-concurrency) 2 | 3 | ```haskell 4 | import Control.Concurrent.STM 5 | 6 | ping :: WriteOnly w => w String -> String -> IO () 7 | ping pings msg = write' pings msg 8 | 9 | pong :: (ReadOnly r, WriteOnly w) => r String -> w String -> IO () 10 | pong pings pongs = do 11 | msg <- read' pings 12 | write' pongs msg 13 | 14 | main = do 15 | pings <- atomically newTQueue 16 | pongs <- atomically newTQueue 17 | ping pings "passed message" 18 | pong pings pongs 19 | putStrLn =<< read' pongs 20 | 21 | class ReadOnly f where 22 | read' :: f a -> IO a 23 | instance ReadOnly TQueue where 24 | read' = atomically . readTQueue 25 | 26 | class WriteOnly f where 27 | write' :: f a -> a -> IO () 28 | instance WriteOnly TQueue where 29 | write' = (atomically.) . writeTQueue 30 | ``` 31 | 32 | ```bash 33 | $ runhaskell channel-directions.hs 34 | passed message 35 | ``` 36 | -------------------------------------------------------------------------------- /ex/channel-synchronization.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Control.Concurrent 3 | import Control.Concurrent.STM 4 | 5 | worker :: TMVar Bool -> IO () 6 | worker done = do 7 | putStr "working..." 8 | threadDelay 1000000 9 | 10 | putStrLn "done" 11 | atomically $ putTMVar done True 12 | 13 | main = do 14 | done <- atomically newEmptyTMVar 15 | forkIO $ worker done 16 | 17 | atomically $ takeTMVar done 18 | return () 19 | ``` 20 | 21 | ```bash 22 | $ runhaskell channel-synchronization.hs 23 | working...done 24 | ``` 25 | -------------------------------------------------------------------------------- /ex/channels.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Control.Concurrent 3 | import Control.Concurrent.STM 4 | 5 | main = do 6 | messages <- atomically newTQueue 7 | 8 | forkIO $ atomically $ writeTQueue messages "ping" 9 | 10 | msg <- atomically $ readTQueue messages 11 | putStrLn msg 12 | ``` 13 | 14 | ```bash 15 | $ runhaskell channels.hs 16 | ping 17 | ``` 18 | -------------------------------------------------------------------------------- /ex/closures.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.IORef 3 | 4 | intSeq :: IORef Int -> IO Int 5 | intSeq ref = do 6 | modifyIORef ref (+1) 7 | readIORef ref 8 | 9 | main = do 10 | ref <- newIORef 0 11 | let nextInt = intSeq ref 12 | 13 | print =<< nextInt 14 | print =<< nextInt 15 | print =<< nextInt 16 | 17 | ref' <- newIORef 0 18 | let newInts = intSeq ref' 19 | print =<< newInts 20 | ``` 21 | 22 | ```bash 23 | $ runhaskell closures.hs 24 | 1 25 | 2 26 | 3 27 | 1 28 | ``` 29 | -------------------------------------------------------------------------------- /ex/collection-functions.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.List 3 | import Data.Char 4 | 5 | main = do 6 | let strs = ["peach", "apple", "pear", "plum"] 7 | 8 | print $ lookup "pear" (zip strs [0..]) 9 | print $ elem "grape" strs 10 | print $ any (isPrefixOf "p") strs 11 | print $ all (isPrefixOf "p") strs 12 | print $ filter (elem 'e') strs 13 | print $ map (map toUpper) strs 14 | ``` 15 | 16 | ```bash 17 | $ runhaskell collection-functions.hs 18 | Just 2 19 | False 20 | True 21 | False 22 | ["peach", "apple", "pear"] 23 | ["PEACH","APPLE","PEAR","PLUM"] 24 | ``` 25 | -------------------------------------------------------------------------------- /ex/command-line-arguments.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import System.Environment 3 | 4 | main = do 5 | args <- getArgs 6 | let arg = args !! 3 7 | 8 | print args 9 | putStrLn arg 10 | 11 | progName <- getProgName 12 | putStrLn progName 13 | ``` 14 | 15 | ```bash 16 | $ ghc command-line-arguments.hs -o command-line-arguments 17 | $ ./command-line-arguments a b c d 18 | ["a","b","c","d"] 19 | d 20 | command-line-arguments 21 | ``` 22 | -------------------------------------------------------------------------------- /ex/command-line-flags.md: -------------------------------------------------------------------------------- 1 | To work this example, you need install [optparse-declarative](http://hackage.haskell.org/package/optparse-declarative). 2 | 3 | ```bash 4 | $ cabal install optparse-declarative 5 | ``` 6 | 7 | ```haskell 8 | {-# LANGUAGE DataKinds #-} 9 | import Options.Declarative 10 | import Control.Monad.IO.Class (liftIO) 11 | 12 | flags :: Flag "" '["word"] "\"foo\"" "a string" (Def "foo" String) 13 | -> Flag "" '["numb"] "42" "an int" (Def "42" Int) 14 | -> Flag "" '["fork"] "false" "a bool" Bool 15 | -> Flag "" '["svar"] "\"bar\"" "a string var" (Def "bar" String) 16 | -> Arg "ARGS" [String] 17 | -> Cmd "Command-Line Flags" () 18 | flags word numb fork svar args = do 19 | liftIO . putStrLn $ "word:" ++ get word 20 | liftIO . putStrLn $ "numb:" ++ (show . get $ numb) 21 | liftIO . putStrLn $ "fork:" ++ (show . get $ fork) 22 | liftIO . putStrLn $ "svar:" ++ get svar 23 | liftIO . putStrLn $ "tail:" ++ (show . get $ args) 24 | 25 | main :: IO () 26 | main = run_ flags 27 | ``` 28 | 29 | ```bash 30 | $ ghc command-line-flags.hs -o command-line-flags 31 | 32 | $ ./command-line-flags --word=opt --numb=7 --fork --svar=flag 33 | word:opt 34 | numb:7 35 | fork:True 36 | svar:flag 37 | tail:[] 38 | 39 | $ ./command-line-flags --word=opt 40 | word:opt 41 | numb:42 42 | fork:False 43 | svar:bar 44 | tail:[] 45 | 46 | $ ./command-line-flags --word=opt a1 a2 a3 47 | word:opt 48 | numb:42 49 | fork:False 50 | svar:bar 51 | tail:["a1","a2","a3"] 52 | 53 | $ ./command-line-flags --word=opt a1 a2 a3 --numb=7 54 | word:opt 55 | numb:42 56 | fork:False 57 | svar:bar 58 | tail:["a1","a2","a3","--numb=7"] 59 | 60 | $ ./command-line-flags -? 61 | Usage: command-line-flags [OPTION...] ARGS 62 | Command-Line Flags 63 | 64 | Options: 65 | --word="foo" a string 66 | --numb=42 an int 67 | --fork a bool 68 | --svar="bar" a string var 69 | -? --help display this help and exit 70 | -v[n] --verbose[=n] set verbosity level 71 | 72 | $ ./command-line-flags --wat 73 | command-line-flags: unrecognized option '--wat' 74 | Try 'command-line-flags --help' for more information. 75 | ``` 76 | -------------------------------------------------------------------------------- /ex/constants.md: -------------------------------------------------------------------------------- 1 | All variables are immutable and constant. 2 | 3 | ```haskell 4 | s :: String 5 | s = "constant" 6 | 7 | main = do 8 | putStrLn s 9 | 10 | let n = 500000000 11 | let d = 3e20 / n 12 | 13 | print d 14 | print $ sin n 15 | ``` 16 | 17 | ```bash 18 | $ runhaskell constants.hs 19 | constant 20 | 6.0e11 21 | -0.284704073237544 22 | ``` 23 | -------------------------------------------------------------------------------- /ex/defer.md: -------------------------------------------------------------------------------- 1 | Use bracket. 2 | 3 | ```haskell 4 | import Control.Exception 5 | import System.IO 6 | import System.IO.Error 7 | 8 | main = bracket (createFile "/tmp/defer.txt") closeFile writeFile' 9 | 10 | createFile :: FilePath -> IO Handle 11 | createFile path = do 12 | putStrLn "creating" 13 | openFile path WriteMode `catch` (error . ioeGetErrorString) 14 | 15 | writeFile' :: Handle -> IO () 16 | writeFile' handle = do 17 | putStrLn "writing" 18 | hPutStr handle "data" 19 | 20 | closeFile :: Handle -> IO () 21 | closeFile handle = do 22 | putStrLn "closing" 23 | hClose handle 24 | ``` 25 | 26 | ```bash 27 | $ runhaskell defer.hs 28 | creating 29 | writing 30 | closing 31 | ``` 32 | -------------------------------------------------------------------------------- /ex/environment-variables.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import System.Environment 3 | 4 | main = do 5 | setEnv "FOO" "1" 6 | putStr "FOO:" >> (putStrLn =<< getEnv "FOO") 7 | putStr "BAR:" >> (print =<< lookupEnv "BAR") 8 | putStrLn "" 9 | mapM_ putStrLn =<< map fst `fmap` getEnvironment 10 | ``` 11 | 12 | ```bash 13 | $ runhaskell environment-variables.hs 14 | FOO:1 15 | BAR:Nothing 16 | 17 | MANPATH 18 | rvm_bin_path 19 | TERM_PROGRAM 20 | ... 21 | 22 | $ BAR=2 runhaskell environment-variables.hs 23 | FOO:1 24 | BAR:Just "2" 25 | ... 26 | ``` 27 | -------------------------------------------------------------------------------- /ex/epoch.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import System.Locale 3 | import Data.Time 4 | 5 | main = do 6 | now <- getCurrentTime 7 | print now 8 | let secs = read $ formatTime defaultTimeLocale "%s" now :: Integer 9 | let picos = read $ formatTime defaultTimeLocale "%s%q" now :: Integer 10 | let nanos = div picos 1000 11 | let millis = div picos 1000000000 12 | print secs 13 | print millis 14 | print nanos 15 | print (parseTime defaultTimeLocale "%s" (show secs) :: Maybe UTCTime) 16 | ``` 17 | 18 | ```bash 19 | $ runhaskell epoch.hs 20 | 2015-05-05 13:32:26.455623 UTC 21 | 1430832746 22 | 1430832746455 23 | 1430832746455623000 24 | Just 2015-05-05 13:32:26 UTC 25 | ``` 26 | -------------------------------------------------------------------------------- /ex/errors.md: -------------------------------------------------------------------------------- 1 | Either failure or success. 2 | 3 | ```haskell 4 | import Control.Monad 5 | 6 | f1 :: Int -> Either String Int 7 | f1 arg = if arg == 42 8 | then Left "can't work with 42" 9 | else Right (arg + 3) 10 | 11 | data ArgError = ArgError Int String 12 | 13 | instance Show ArgError where 14 | show (ArgError arg err) = show arg ++ " - " ++ err 15 | 16 | f2 :: Int -> Either ArgError Int 17 | f2 arg = if arg == 42 18 | then Left (ArgError arg "can't work with it") 19 | else Right (arg + 3) 20 | 21 | main = do 22 | forM_ [7, 42] $ \i -> do 23 | case f1 i of 24 | Left error -> putStrLn $ "f1 failed: " ++ error 25 | Right value -> putStrLn $ "f1 worked: " ++ show value 26 | 27 | forM_ [7, 42] $ \i -> do 28 | case f2 i of 29 | Left err -> putStrLn $ "f2 failed: " ++ show err 30 | Right value -> putStrLn $ "f2 worked: " ++ show value 31 | case f2 42 of 32 | Left (ArgError arg err) -> do 33 | print arg 34 | putStrLn err 35 | _ -> return () 36 | ``` 37 | 38 | ```bash 39 | $ runhaskell errors.hs 40 | f1 worked: 10 41 | f1 failed: can't work with 42 42 | f2 worked: 10 43 | f2 failed: 42 - can't work with it 44 | 42 45 | can't work with it 46 | ``` 47 | -------------------------------------------------------------------------------- /ex/execing-processes.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import System.Process 3 | import System.Directory 4 | 5 | main = do 6 | path <- findExecutable "ls" 7 | case path of 8 | Nothing -> error "ls doesn't exist" 9 | Just _ -> createProcess (proc "ls" ["-a", "-l", "-h"]) 10 | ``` 11 | 12 | ```bash 13 | $ runhaskell execing-processes.hs 14 | total 8 15 | drwxr-xr-x 3 staff 102B 5 5 23:00 . 16 | drwxr-xr-x 63 staff 2.1K 5 5 22:58 .. 17 | -rw-r--r-- 1 staff 214B 5 5 23:00 execing-processes.hs 18 | ``` 19 | -------------------------------------------------------------------------------- /ex/exit.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import System.Exit 3 | 4 | main = do 5 | exitWith $ ExitFailure 3 6 | putStrLn "!" 7 | ``` 8 | 9 | ```bash 10 | $ runhaskell exit.hs 11 | 12 | $ ghc exit.hs -o exit 13 | $ ./exit 14 | $ echo $? 15 | 3 16 | ``` 17 | -------------------------------------------------------------------------------- /ex/for.md: -------------------------------------------------------------------------------- 1 | `forM_ = flip mapM_` 2 | 3 | ```haskell 4 | import Control.Monad.Cont 5 | 6 | main = do 7 | forM_ [1..3] $ \i -> do 8 | print i 9 | 10 | forM_ [7..9] $ \j -> do 11 | print j 12 | 13 | withBreak $ \break -> 14 | forM_ [1..] $ \_ -> do 15 | p "loop" 16 | break () 17 | 18 | where 19 | withBreak = (`runContT` return) . callCC 20 | p = liftIO . putStrLn 21 | ``` 22 | 23 | ```bash 24 | $ runhaskell for.hs 25 | 1 26 | 2 27 | 3 28 | 7 29 | 8 30 | 9 31 | loop 32 | ``` 33 | -------------------------------------------------------------------------------- /ex/functions.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | plus :: Int -> Int -> Int 3 | plus = (+) 4 | 5 | plusPlus :: Int -> Int -> Int -> Int 6 | plusPlus a b c = a + b + c 7 | 8 | main = do 9 | let res = plus 1 2 10 | putStrLn $ "1+2 = " ++ show res 11 | 12 | let res = plusPlus 1 2 3 13 | putStrLn $ "1+2+3 = " ++ show res 14 | ``` 15 | 16 | ```bash 17 | $ runhaskell functions.hs 18 | 1+2 = 3 19 | 1+2+3 = 6 20 | ``` 21 | -------------------------------------------------------------------------------- /ex/goroutines.md: -------------------------------------------------------------------------------- 1 | Actually there is no goroutine in Haskell. 2 | Instead, This example uses forkIO. 3 | It is a common way to use concurrency in Haskell. 4 | 5 | ```haskell 6 | import Control.Monad 7 | import Control.Concurrent 8 | 9 | f :: String -> IO () 10 | f from = forM_ [0..2] (\i -> putStrLn $ from ++ ":" ++ show i) 11 | 12 | main = do 13 | f "direct" 14 | forkIO $ f "forkIO" 15 | forkIO $ (\msg -> putStrLn msg) "going" 16 | 17 | threadDelay 1000000 18 | putStrLn "done" 19 | ``` 20 | 21 | ```bash 22 | $ runhaskell goroutines.hs 23 | direct:0 24 | direct:1 25 | direct:2 26 | going 27 | forkIO:0 28 | forkIO:1 29 | forkIO:2 30 | done 31 | ``` 32 | -------------------------------------------------------------------------------- /ex/hello-world.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | main = putStrLn "hello world" 3 | ``` 4 | 5 | ```bash 6 | $ runhaskell hello-world.hs 7 | hello world 8 | 9 | $ ghc hello-world.hs -o hello-world 10 | $ ls 11 | hello-world hello-world.hi hello-world.hs hello-world.o 12 | 13 | $ ./hello-world 14 | hello world 15 | ``` 16 | -------------------------------------------------------------------------------- /ex/if-else.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | main = do 3 | if 7 `mod` 2 == 0 4 | then putStrLn "7 is even" 5 | else putStrLn "7 is odd" 6 | 7 | if 8 `mod` 4 == 0 8 | then putStrLn "8 is divisible by 4" 9 | else return () 10 | 11 | let num = 9 12 | putStrLn $ 13 | if num < 0 14 | then show num ++ " is negative" 15 | else if num < 10 16 | then show num ++ " has 1 digit" 17 | else show num ++ " has multiple digits" 18 | ``` 19 | 20 | ```bash 21 | $ runhaskell if-else.hs 22 | 7 is odd 23 | 8 is divisible by 4 24 | 9 has 1 digit 25 | ``` 26 | -------------------------------------------------------------------------------- /ex/interfaces.md: -------------------------------------------------------------------------------- 1 | Type Classes and Class Methods. 2 | 3 | ```haskell 4 | class Geometry g where 5 | area :: g -> Double 6 | perim :: g -> Double 7 | 8 | data Square = Square Double Double deriving Show 9 | data Circle = Circle Double deriving Show 10 | 11 | instance Geometry Square where 12 | area (Square w h) = w * h 13 | perim (Square w h) = 2 * w + 2 * h 14 | 15 | instance Geometry Circle where 16 | area (Circle r) = pi * r * r 17 | perim (Circle r) = 2 * pi * r 18 | 19 | measure :: (Geometry a, Show a) => a -> IO () 20 | measure g = do 21 | print g 22 | print $ area g 23 | print $ perim g 24 | 25 | main = do 26 | let s = Square 3 4 27 | let c = Circle 5 28 | 29 | measure s 30 | measure c 31 | ``` 32 | 33 | ```bash 34 | $ runhaskell interfaces.hs 35 | Square 3.0 4.0 36 | 12.0 37 | 14.0 38 | Circle 5.0 39 | 78.53981633974483 40 | 31.41592653589793 41 | ``` 42 | -------------------------------------------------------------------------------- /ex/json.md: -------------------------------------------------------------------------------- 1 | To work this example, you need install [aeson](https://hackage.haskell.org/package/aeson) and [lens-aeson](https://hackage.haskell.org/package/lens-aeson). 2 | 3 | ```bash 4 | $ cabal install aeson lens-aeson 5 | ``` 6 | 7 | ```haskell 8 | {-# LANGUAGE DeriveGeneric, OverloadedStrings #-} 9 | import GHC.Generics 10 | import Control.Lens 11 | import Data.Aeson 12 | import Data.Aeson.Lens 13 | import qualified Data.Map as Map 14 | import qualified Data.ByteString.Lazy.Char8 as BS 15 | 16 | data Response1 = Response1 { page :: Int 17 | , fruits :: [String] 18 | } deriving (Show, Generic) 19 | instance FromJSON Response1 20 | instance ToJSON Response1 21 | 22 | main = do 23 | BS.putStrLn $ encode True 24 | BS.putStrLn $ encode (1 :: Int) 25 | BS.putStrLn $ encode (2.34 :: Double) 26 | BS.putStrLn $ encode ("haskell" :: String) 27 | BS.putStrLn $ encode (["apple", "peach", "pear"] :: [String]) 28 | BS.putStrLn $ encode $ Map.fromList ([("apple", 5), ("lettuce", 7)] :: [(String, Int)]) 29 | BS.putStrLn $ encode $ Response1 {page = 1, fruits = ["apple", "peach", "pear"]} 30 | 31 | let byt = "{\"num\":6.13,\"strs\":[\"a\",\"b\"]}" 32 | let Just dat = decode byt :: Maybe Value 33 | print dat 34 | let Just num = dat ^? key "num" 35 | print num 36 | let Just str1 = dat ^? key "strs" . nth 0 37 | print str1 38 | 39 | let str = "{\"page\": 1, \"fruits\": [\"apple\", \"peach\"]}" 40 | let Just res = decode str :: Maybe Response1 41 | print res 42 | putStrLn $ (fruits res) !! 0 43 | ``` 44 | 45 | ```bash 46 | $ runhaskell json.hs 47 | true 48 | 1 49 | 2.34 50 | "haskell" 51 | ["apple","peach","pear"] 52 | {"lettuce":7,"apple":5} 53 | {"fruits":["apple","peach","pear"],"page":1} 54 | Object (fromList [("num",Number 6.13),("strs",Array (fromList [String "a",String "b"]))]) 55 | Number 6.13 56 | String "a" 57 | Response1 {page = 1, fruits = ["apple","peach"]} 58 | apple 59 | ``` 60 | -------------------------------------------------------------------------------- /ex/line-filters.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.Char 3 | 4 | main = interact $ fmap toUpper 5 | ``` 6 | 7 | ```bash 8 | $ echo 'hello' > /tmp/lines 9 | $ echo 'filter' >> /tmp/lines 10 | $ cat /tmp/lines | runhaskell line-filters.hs 11 | HELLO 12 | FILTER 13 | ``` 14 | -------------------------------------------------------------------------------- /ex/maps.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.Map (Map, (!)) 3 | import qualified Data.Map as Map 4 | 5 | main = do 6 | let m0 = Map.empty 7 | let m1 = Map.insert "k1" 7 m0 8 | let m = Map.insert "k2" 13 m1 9 | putStrLn $ "map: " ++ show m 10 | 11 | let v1 = m ! "k1" 12 | putStrLn $ "v1: " ++ show v1 13 | putStrLn $ "len: " ++ show (Map.size m) 14 | 15 | let m' = Map.delete "k2" m 16 | putStrLn $ "map: " ++ show m' 17 | 18 | let prs = Map.lookup "k2" m' 19 | putStrLn $ "prs: " ++ show prs 20 | 21 | let n = Map.fromList [("foo", 1), ("bar", 2)] 22 | putStrLn $ "map: " ++ show n 23 | ``` 24 | 25 | ```bash 26 | $ runhaskell maps.hs 27 | map: fromList [("k1",7),("k2",13)] 28 | v1: 7 29 | len: 2 30 | map: fromList [("k1",7)] 31 | prs: Nothing 32 | map: fromList [("bar",2),("foo",1)] 33 | ``` 34 | -------------------------------------------------------------------------------- /ex/methods.md: -------------------------------------------------------------------------------- 1 | Just use functions. 2 | 3 | ```haskell 4 | data Rect = Rect Int Int 5 | 6 | area :: Rect -> Int 7 | area (Rect w h) = w * h 8 | 9 | perim :: Rect -> Int 10 | perim (Rect w h) = 2 * w + 2 * h 11 | 12 | main = do 13 | let r = Rect 10 5 14 | putStrLn $ "area: " ++ show (area r) 15 | putStrLn $ "perim: " ++ show (perim r) 16 | ``` 17 | 18 | ```bash 19 | $ runhaskell methods.hs 20 | area: 50 21 | perim: 30 22 | ``` 23 | -------------------------------------------------------------------------------- /ex/multiple-return-values.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | vals :: () -> (Int, Int) 3 | vals () = (3, 7) 4 | 5 | main = do 6 | let (a, b) = vals () 7 | print a 8 | print b 9 | 10 | let (_, c) = vals () 11 | print c 12 | ``` 13 | 14 | ```bash 15 | $ runhaskell multiple-return-values.hs 16 | 3 17 | 7 18 | 7 19 | ``` 20 | -------------------------------------------------------------------------------- /ex/mutexes.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Control.Monad 3 | import Control.Concurrent 4 | import Control.Concurrent.STM 5 | import Data.Maybe 6 | import qualified Data.Map as Map 7 | import System.Random 8 | 9 | main = do 10 | state <- atomically $ newTVar Map.empty 11 | ops <- atomically $ newTVar 0 12 | 13 | forM_ [0..99] $ \_ -> do 14 | total <- atomically $ newTVar 0 15 | forkIO . forever $ do 16 | key <- randomRIO (0, 4) :: IO Int 17 | atomically $ do 18 | s <- readTVar state 19 | writeTVar state s 20 | let v = maybe 0 id $ Map.lookup key s 21 | modifyTVar total (+v) 22 | modifyTVar ops (+1) 23 | 24 | forM_ [0..9] $ \_ -> do 25 | forkIO . forever $ do 26 | key <- randomRIO (0, 4) :: IO Int 27 | val <- randomRIO (0, 99) :: IO Int 28 | atomically $ do 29 | modifyTVar state (Map.insert key val) 30 | modifyTVar ops (+1) 31 | 32 | threadDelay 1000000 33 | opsFinal <- atomically $ readTVar ops 34 | putStrLn $ "ops: " ++ show opsFinal 35 | 36 | s <- atomically $ readTVar state 37 | putStrLn $ "state: " ++ show s 38 | ``` 39 | 40 | ```bash 41 | $ runhaskell mutexes.hs 42 | ops: 299042 43 | state: fromList [(0,50),(1,81),(2,55),(3,87),(4,47)] 44 | ``` 45 | -------------------------------------------------------------------------------- /ex/non-blocking-channel-operations.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | {-# LANGUAGE GADTs #-} 3 | import Control.Concurrent 4 | import Control.Concurrent.STM 5 | 6 | main = do 7 | messages <- atomically $ newEmptyTMVar :: IO (TMVar String) 8 | signals <- atomically $ newEmptyTMVar :: IO (TMVar Bool) 9 | 10 | trymsg <- atomically $ tryReadTMVar messages 11 | case trymsg of 12 | Just m -> putStrLn $ "received message " ++ m 13 | Nothing -> putStrLn "no message received" 14 | 15 | let msg = "hi" 16 | success <- atomically $ tryPutTMVar messages msg 17 | if success 18 | then putStrLn $ "sent message " ++ msg 19 | else putStrLn "no message sent" 20 | 21 | select [ Case messages $ \msg -> putStrLn $ "received message " ++ msg 22 | , Case signals $ \sig -> putStrLn $ "received signal " ++ show sig 23 | , Default $ putStrLn "no activiry" 24 | ] 25 | 26 | class Selectable f where 27 | tryRead :: f a -> STM (Maybe a) 28 | 29 | instance Selectable TMVar where 30 | tryRead = tryReadTMVar 31 | 32 | data Select a where 33 | Default :: IO a -> Select a 34 | Case :: Selectable s => s b -> (b -> IO a) -> Select a 35 | 36 | select :: [Select a] -> IO a 37 | select [] = error "select: empty list" 38 | select ((Default x):_) = x 39 | select (x@(Case v f):xs) = do 40 | var <- atomically $ tryRead v 41 | case var of 42 | Just b -> f b 43 | Nothing -> select (xs ++ [x]) 44 | ``` 45 | 46 | ```bash 47 | $ runhaskell non-blocking-channel-operations.hs 48 | no message received 49 | sent message hi 50 | received message hi 51 | ``` 52 | -------------------------------------------------------------------------------- /ex/number-parsing.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | main = do 3 | print (read "1.234" :: Double) 4 | print (read "123" :: Int) 5 | print (read "0x1c8" :: Int) 6 | print (read "wat" :: Int) 7 | ``` 8 | 9 | ```bash 10 | $ runhaskell number-parsing.hs 11 | 1.234 12 | 123 13 | 456 14 | number-parsing.hs: Prelude.read: no parse 15 | ``` 16 | -------------------------------------------------------------------------------- /ex/panic.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | main = error "a problem" 3 | ``` 4 | 5 | ```bash 6 | $ runhaskell panic.hs 7 | panic.hs: a problem 8 | ``` 9 | -------------------------------------------------------------------------------- /ex/random-numbers.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import System.Random 3 | 4 | main = do 5 | putStr . show =<< randomRIO (0, 100 :: Int) 6 | putStr ", " 7 | print =<< randomRIO (0, 100 :: Int) 8 | 9 | print =<< (randomIO :: IO Float) 10 | 11 | f1 <- randomIO :: IO Float 12 | putStr $ show (f1 * 5 + 5) ++ ", " 13 | f2 <- randomIO :: IO Float 14 | print $ f2 * 5 + 5 15 | 16 | let s1 = mkStdGen 42 17 | let (i1, s2) = randomR (0, 100 :: Int) s1 18 | let (i2, _) = randomR (0, 100 :: Int) s2 19 | putStrLn $ show i1 ++ ", " ++ show i2 20 | 21 | let s3 = mkStdGen 42 22 | let (i3, s4) = randomR (0, 100 :: Int) s3 23 | let (i4, _) = randomR (0, 100 :: Int) s4 24 | putStrLn $ show i3 ++ ", " ++ show i4 25 | ``` 26 | 27 | ```bash 28 | $ runhaskell random-numbers.hs 29 | 51, 15 30 | 0.2895795 31 | 9.823023, 5.912962 32 | 77, 38 33 | 77, 38 34 | ``` 35 | -------------------------------------------------------------------------------- /ex/range.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Control.Monad 3 | import Data.Map (Map) 4 | import qualified Data.Map as Map 5 | 6 | main = do 7 | let nums = [2, 3, 4] 8 | putStrLn $ "sum: " ++ show (sum nums) 9 | 10 | mapM_ putStrLn ["index: " ++ show i | (i, num) <- zip [0..] nums, num == 3] 11 | 12 | let kvs = Map.fromList [("a", "apple"), ("b", "banana")] 13 | forM_ (Map.toList kvs) $ \(k, v) -> putStrLn $ k ++ " -> " ++ v 14 | 15 | mapM_ print $ zip [0..] "haskell" 16 | ``` 17 | 18 | ```bash 19 | $ runhaskell range.hs 20 | sum: 9 21 | index: 1 22 | a -> apple 23 | b -> banana 24 | (0,'h') 25 | (1,'a') 26 | (2,'s') 27 | (3,'k') 28 | (4,'e') 29 | (5,'l') 30 | (6,'l') 31 | ``` 32 | -------------------------------------------------------------------------------- /ex/rate-limiting.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.Time 3 | import Control.Monad 4 | import Control.Concurrent 5 | import Control.Concurrent.STM 6 | 7 | main = do 8 | requests <- atomically $ do 9 | req <- newTQueue 10 | mapM_ (writeTQueue req) [1..5] 11 | return req 12 | 13 | limitter <- atomically $ newEmptyTMVar 14 | forkIO . forever $ do 15 | atomically $ putTMVar limitter () 16 | threadDelay (200 * 1000) 17 | 18 | let loop1 = do 19 | req <- atomically $ do 20 | r <- readTQueue requests 21 | takeTMVar limitter 22 | return r 23 | now <- getCurrentTime 24 | putStrLn $ "request " ++ show req ++ " " ++ show now 25 | isEmpty <- atomically $ isEmptyTQueue requests 26 | if isEmpty 27 | then return () 28 | else loop1 29 | loop1 30 | 31 | now <- getCurrentTime 32 | burstyLimitter <- atomically $ do 33 | limitter <- newTBQueue 3 34 | forM_ [0..2] $ \_ -> writeTBQueue limitter now 35 | return limitter 36 | 37 | forkIO . forever $ do 38 | now <- getCurrentTime 39 | atomically $ writeTBQueue burstyLimitter now 40 | threadDelay (200 * 1000) 41 | 42 | burstyRequests <- atomically $ do 43 | req <- newTQueue 44 | mapM_ (writeTQueue req) [1..5] 45 | return req 46 | 47 | let loop2 = do 48 | req <- atomically $ do 49 | r <- readTQueue burstyRequests 50 | readTBQueue burstyLimitter 51 | return r 52 | now <- getCurrentTime 53 | putStrLn $ "request " ++ show req ++ " " ++ show now 54 | isEmpty <- atomically $ isEmptyTQueue burstyRequests 55 | if isEmpty 56 | then return () 57 | else loop2 58 | loop2 59 | ``` 60 | 61 | ```bash 62 | $ runhaskell rate-limiting.hs 63 | request 1 2018-10-18 21:22:30.873766 UTC 64 | request 2 2018-10-18 21:22:31.074185 UTC 65 | request 3 2018-10-18 21:22:31.274635 UTC 66 | request 4 2018-10-18 21:22:31.474943 UTC 67 | request 5 2018-10-18 21:22:31.675533 UTC 68 | request 1 2018-10-18 21:22:31.675713 UTC 69 | request 2 2018-10-18 21:22:31.675839 UTC 70 | request 3 2018-10-18 21:22:31.675947 UTC 71 | request 4 2018-10-18 21:22:31.676057 UTC 72 | request 5 2018-10-18 21:22:31.876147 UTC 73 | ``` 74 | -------------------------------------------------------------------------------- /ex/reading-files.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import System.IO 3 | 4 | readAtLeast :: Handle -> Integer -> IO String 5 | readAtLeast handle n 6 | | n <= 0 = return "" 7 | | otherwise = do 8 | eof <- hIsEOF handle 9 | if eof 10 | then return "" 11 | else do 12 | c <- hGetChar handle 13 | str <- readAtLeast handle (n-1) 14 | return (c:str) 15 | 16 | main = do 17 | dat <- readFile "/tmp/dat" 18 | putStrLn dat 19 | 20 | handle <- openFile "/tmp/dat" ReadMode 21 | 22 | b1 <- readAtLeast handle 5 23 | putStrLn $ show (length b1) ++ " bytes: " ++ b1 24 | 25 | hSeek handle AbsoluteSeek 6 26 | at2 <- hTell handle 27 | b2 <- sequence . take 7 . repeat $ hGetChar handle 28 | putStrLn $ show (length b2) ++ " bytes @ " ++ show at2 ++ ": " ++ b2 29 | 30 | hSeek handle AbsoluteSeek 6 31 | at3 <- hTell handle 32 | b3 <- readAtLeast handle 7 33 | putStrLn $ show (length b3) ++ " bytes @ " ++ show at3 ++ ": " ++ b3 34 | 35 | hSeek handle AbsoluteSeek 0 36 | hSetBuffering handle (BlockBuffering Nothing) -- default 37 | b3 <- readAtLeast handle 5 38 | putStrLn $ show (length b3) ++ " bytes: " ++ b3 39 | 40 | hClose handle 41 | ``` 42 | 43 | ```bash 44 | $ echo "hello" > /tmp/dat 45 | $ echo "haskell" >> /tmp/dat 46 | $ runhaskell reading-files.hs 47 | hello 48 | haskell 49 | 50 | 5 bytes: hello 51 | 7 bytes @ 6: haskell 52 | 7 bytes @ 6: haskell 53 | 5 bytes: hello 54 | ``` 55 | -------------------------------------------------------------------------------- /ex/recursion.md: -------------------------------------------------------------------------------- 1 | see also [The Evolution of a Haskell Programmer](http://www.willamette.edu/~fruehr/haskell/evolution.html). 2 | 3 | ```haskell 4 | fact :: Int -> Int 5 | fact 0 = 1 6 | fact n = n * fact (n-1) 7 | 8 | main = print $ fact 7 9 | ``` 10 | 11 | ```bash 12 | $ runhaskell recursion.hs 13 | 5040 14 | ``` 15 | -------------------------------------------------------------------------------- /ex/regular-expressions.md: -------------------------------------------------------------------------------- 1 | To work this example, you need install [regex-compat-tdfa](https://hackage.haskell.org/package/regex-compat-tdfa). 2 | 3 | ```bash 4 | $ cabal install regex-compat-tdfa 5 | ``` 6 | 7 | ```haskell 8 | import Data.Array 9 | import Text.Regex 10 | import Text.Regex.Base 11 | 12 | main = do 13 | let mch = matchTest (mkRegex "p([a-z]+)ch") "peach" 14 | print mch 15 | 16 | let r = mkRegex "p([a-z]+)ch" 17 | 18 | -- MatchString 19 | print $ matchTest r "peach" 20 | 21 | -- FindString 22 | putStrLn $ fst . (!0) . head $ matchAllText r "peach punch" 23 | 24 | -- FindStringIndex 25 | print $ snd . (!0) . head $ matchAllText r "peach punch" 26 | 27 | -- FindStringSubmatch 28 | print $ map fst . elems . head $ matchAllText r "peach punch" 29 | 30 | -- FindStringSubmatchIndex 31 | print $ map snd . elems . head $ matchAllText r "peach punch" 32 | 33 | -- FindAllString 34 | print $ map (fst . head . elems) $ matchAllText r "peach punch pinch" 35 | 36 | -- FindAllStringSubmatchIndex 37 | print $ map (map snd . elems) $ matchAllText r "peach punch pinch" 38 | 39 | -- FindAllString (2) 40 | print $ take 2 . map (fst . head . elems) $ matchAllText r "peach punch pinch" 41 | 42 | -- ReplaceAllString 43 | putStrLn $ subRegex r "a peach" "" 44 | ``` 45 | 46 | ```bash 47 | $ runhaskell regular-expressions.hs 48 | True 49 | True 50 | peach 51 | (0,5) 52 | ["peach","ea"] 53 | [(0,5),(1,2)] 54 | ["peach","punch","pinch"] 55 | [[(0,5),(1,2)],[(6,5),(7,2)],[(12,5),(13,2)]] 56 | ["peach","punch"] 57 | a 58 | ``` 59 | -------------------------------------------------------------------------------- /ex/select.md: -------------------------------------------------------------------------------- 1 | Actually, there is no select statement in Haskell. I implement a makeshift "select" in this example. If you have a better idea, please send a Pull Request to [my repository](https://github.com/lotz84/haskellbyexample)! 2 | 3 | ```haskell 4 | {-# LANGUAGE GADTs #-} 5 | import Control.Monad 6 | import Control.Concurrent 7 | import Control.Concurrent.STM 8 | 9 | main = do 10 | c1 <- atomically newTQueue 11 | c2 <- atomically newTQueue 12 | 13 | forkIO $ do 14 | threadDelay (1 * 1000000) 15 | atomically $ writeTQueue c1 "one" 16 | 17 | forkIO $ do 18 | threadDelay (2 * 1000000) 19 | atomically $ writeTQueue c2 "two" 20 | 21 | forM_ [0..1] $ \i -> 22 | select [ Case c1 $ \msg1 -> putStrLn $ "received " ++ msg1 23 | , Case c2 $ \msg2 -> putStrLn $ "received " ++ msg2 24 | ] 25 | 26 | class Selectable f where 27 | tryRead :: f a -> STM (Maybe a) 28 | 29 | instance Selectable TQueue where 30 | tryRead = tryReadTQueue 31 | 32 | data Select a where 33 | Default :: IO a -> Select a 34 | Case :: Selectable s => s b -> (b -> IO a) -> Select a 35 | 36 | select :: [Select a] -> IO a 37 | select [] = error "select: empty list" 38 | select ((Default x):_) = x 39 | select (x@(Case v f):xs) = do 40 | var <- atomically $ tryRead v 41 | case var of 42 | Just b -> f b 43 | Nothing -> select (xs ++ [x]) 44 | ``` 45 | 46 | ```bash 47 | $ time runhaskell select.hs 48 | received one 49 | received two 50 | 51 | real 0m2.267s 52 | ``` 53 | -------------------------------------------------------------------------------- /ex/sha1-hashes.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | {-# LANGUAGE OverloadedStrings #-} 3 | import Numeric (showHex) 4 | import qualified Data.ByteString as BS 5 | import qualified Crypto.Hash.SHA1 as SHA1 6 | 7 | main = do 8 | let s = "sha1 this string" 9 | let bs = SHA1.hash s 10 | 11 | print s 12 | putStrLn $ concatMap (flip showHex "") $ BS.unpack bs 13 | ``` 14 | 15 | ```bash 16 | $ runhaskell sha1-hashes.hs 17 | "sha1 this string" 18 | cf23df227d99a74fbe169e3eba035e633b65d94 19 | ``` 20 | -------------------------------------------------------------------------------- /ex/signals.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import System.Exit 3 | import System.Posix.Signals 4 | import Control.Concurrent 5 | 6 | main :: IO () 7 | main = do 8 | done <- newEmptyMVar 9 | let handler = do 10 | putStrLn "" 11 | putStrLn "interrupt" 12 | putMVar done () 13 | installHandler keyboardSignal (Catch handler) Nothing 14 | putStrLn "awaiting signal" 15 | takeMVar done 16 | putStrLn "exiting" 17 | ``` 18 | 19 | ```bash 20 | $ runhaskell signals.hs 21 | awaiting signal 22 | ^C 23 | interrupt 24 | exiting 25 | ``` 26 | -------------------------------------------------------------------------------- /ex/slices.md: -------------------------------------------------------------------------------- 1 | About Lists 2 | 3 | ```haskell 4 | main = do 5 | let s = [] :: [String] 6 | putStrLn $ "emp: " ++ show s 7 | 8 | let s' = ["a", "b", "c"] 9 | putStrLn $ "set: " ++ show s' 10 | putStrLn $ "get: " ++ s' !! 2 11 | putStrLn $ "len: " ++ show (length s') 12 | 13 | let s2 = s' ++ ["d"] 14 | let s3 = s2 ++ ["d", "f"] 15 | putStrLn $ "apd: " ++ show s3 16 | 17 | let c = s3 18 | putStrLn $ "cpy: " ++ show c 19 | 20 | let l1 = drop 2 . take 5 $ s3 21 | putStrLn $ "sl1: " ++ show l1 22 | 23 | let l2 = take 5 s3 24 | putStrLn $ "sl2: " ++ show l2 25 | 26 | let l3 = drop 2 s3 27 | putStrLn $ "sl3: " ++ show l3 28 | 29 | let t = ["g", "h", "i"] 30 | putStrLn $ "dcl: " ++ show t 31 | 32 | let twoD = [[i + j | j <- [0..i]] | i <- [0..2]] 33 | putStrLn $ "2d: " ++ show twoD 34 | ``` 35 | 36 | ```bash 37 | $ runhaskell slices.hs 38 | emp: [] 39 | set: ["a","b","c"] 40 | get: c 41 | len: 3 42 | apd: ["a","b","c","d","d","f"] 43 | cpy: ["a","b","c","d","d","f"] 44 | sl1: ["c","d","d"] 45 | sl2: ["a","b","c","d","d"] 46 | sl3: ["c","d","d","f"] 47 | dcl: ["g","h","i"] 48 | 2d: [[0],[1,2],[2,3,4]] 49 | ``` 50 | -------------------------------------------------------------------------------- /ex/sorting-by-functions.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.List 3 | import Data.Function 4 | 5 | main = do 6 | let fruits = ["peach", "banana", "kiwi"] 7 | print $ sortBy (compare `on` length) fruits 8 | ``` 9 | 10 | ```bash 11 | $ runhaskell sorting-by-functions.hs 12 | ["kiwi","peach","banana"] 13 | ``` 14 | -------------------------------------------------------------------------------- /ex/sorting.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.List 3 | 4 | main = do 5 | let strs = "cab" 6 | putStrLn $ "Strings: " ++ sort strs 7 | 8 | let ints = [7, 2, 4] 9 | putStrLn $ "Ints: " ++ show (sort ints) 10 | 11 | let s = ints == sort ints 12 | putStrLn $ "Sorted: " ++ show s 13 | ``` 14 | 15 | ```bash 16 | runhaskell sorting.hs 17 | Strings: abc 18 | Ints: [2,4,7] 19 | Sorted: False 20 | ``` 21 | -------------------------------------------------------------------------------- /ex/spawning-processes.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import System.IO 3 | import System.Process 4 | 5 | main = do 6 | (_, Just hout, _, _) <- createProcess (proc "date" []){ std_out = CreatePipe } 7 | dateOut <- hGetContents hout 8 | putStrLn "> date" 9 | putStrLn dateOut 10 | 11 | (Just hin, Just hout, _, _) <- createProcess (proc "grep" ["hello"]){ std_in = CreatePipe, std_out = CreatePipe } 12 | hPutStr hin "hello grep\ngoodbye grep" 13 | grepBytes <- hGetContents hout 14 | putStrLn "> grep hello" 15 | putStrLn grepBytes 16 | 17 | (_, Just hout, _, _) <- createProcess (proc "bash" ["-c", "ls -a -l -h"]){ std_out = CreatePipe } 18 | lsOut <- hGetContents hout 19 | putStrLn "> ls -a -l -h" 20 | putStrLn lsOut 21 | ``` 22 | 23 | ```bash 24 | $ runhaskell spawning-processes.hs 25 | > date 26 | 2015年 5月 5日 火曜日 22時58分59秒 JST 27 | 28 | > grep hello 29 | hello grep 30 | 31 | > ls -a -l -h 32 | total 8 33 | drwxr-xr-x 3 staff 102B 5 5 22:58 . 34 | drwxr-xr-x 63 staff 2.1K 5 5 22:58 .. 35 | -rw-r--r-- 1 staff 639B 5 5 22:58 spawning-processes.hs 36 | ``` 37 | -------------------------------------------------------------------------------- /ex/stateful-goroutines.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | {-# LANGUAGE GADTs #-} 3 | import Control.Monad 4 | import Control.Concurrent 5 | import Control.Concurrent.STM 6 | import System.Random 7 | import Data.IORef 8 | import qualified Data.Map as Map 9 | 10 | data ReadOp = ReadOp { readKey :: Int 11 | , readResp :: MVar Int 12 | } 13 | 14 | data WriteOp = WriteOp { writeKey :: Int 15 | , writeVal :: Int 16 | , writeResp :: MVar Bool 17 | } 18 | 19 | main = do 20 | ops <- atomically $ newTVar 0 21 | 22 | reads <- newEmptyMVar 23 | writes <- newEmptyMVar 24 | 25 | forkIO $ do 26 | state <- newIORef Map.empty 27 | forever $ do 28 | select [ Case reads $ \read -> do 29 | s <- readIORef state 30 | let val = maybe 0 id $ Map.lookup (readKey read) s 31 | putMVar (readResp read) val 32 | , Case writes $ \write -> do 33 | modifyIORef state (Map.insert (writeKey write) (writeVal write)) 34 | putMVar (writeResp write) True 35 | ] 36 | 37 | forM_ [0..99] $ \_ -> do 38 | forkIO . forever $ do 39 | key <- randomRIO (0,4) 40 | resp <- newEmptyMVar 41 | let read = ReadOp { readKey = key, readResp = resp } 42 | putMVar reads read 43 | takeMVar resp 44 | 45 | atomically $ do 46 | x <- readTVar ops 47 | writeTVar ops (x + 1) 48 | 49 | forM_ [0..9] $ \_ -> do 50 | forkIO . forever $ do 51 | key <- randomRIO (0,4) 52 | val <- randomRIO (0,99) 53 | resp <- newEmptyMVar 54 | let write = WriteOp { writeKey = key, writeVal = val, writeResp = resp } 55 | putMVar writes write 56 | takeMVar resp 57 | 58 | atomically $ do 59 | x <- readTVar ops 60 | writeTVar ops (x + 1) 61 | 62 | threadDelay 1000000 63 | 64 | opsFinal <- atomically $ readTVar ops 65 | putStrLn $ "ops: " ++ show opsFinal 66 | 67 | data Select a where 68 | Default :: IO a -> Select a 69 | Case :: MVar b -> (b -> IO a) -> Select a 70 | 71 | select :: [Select a] -> IO a 72 | select [] = error "select: empty list" 73 | select ((Default x):_) = x 74 | select (x@(Case v f):xs) = do 75 | var <- tryTakeMVar v 76 | case var of 77 | Just b -> f b 78 | Nothing -> select (xs ++ [x]) 79 | ``` 80 | 81 | ```bash 82 | $ runhaskell stateful-goroutines.hs 83 | ops: 11605 84 | ``` 85 | -------------------------------------------------------------------------------- /ex/string-formatting.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | {-# LANGUAGE OverloadedStrings #-} 3 | import Data.Char (chr) 4 | import System.IO 5 | import Formatting 6 | 7 | data Point = Point { x :: Int 8 | , y :: Int 9 | } deriving Show 10 | 11 | main = do 12 | let p = Point 1 2 13 | 14 | fprint (shown%"\n") p 15 | fprint (shown%"\n") True 16 | fprint (int%"\n") 123 17 | fprint (bin%"\n") 14 18 | fprint (char%"\n") $ chr 33 19 | fprint (hex%"\n") 456 20 | fprint (float%"\n") 78.9 21 | fprint ((expt 4)%"\n") 123400000.0 22 | fprint (text%"\n") "string" 23 | fprint (shown%"\n") "string" 24 | fprint ("|"%(left 6 ' ')%"|"%(left 6 ' ')%"|\n") (12 :: Int) (345 :: Int) 25 | fprint ("|"%(prec 5)%"|"%(prec 5)%"|\n") (1.2 :: Double) (3.45 :: Double) 26 | fprint ("|"%(left 6 ' ')%"|"%(left 6 ' ')%"|\n") ("foo" :: String) ("b" :: String) 27 | fprint ("|"%(right 6 ' ')%"|"%(right 6 ' ')%"|\n") ("foo" :: String) ("b" :: String) 28 | 29 | let s = format ("a "%string) "string" 30 | print s 31 | 32 | hPrint stderr $ format ("an "%string) "error" 33 | ``` 34 | -------------------------------------------------------------------------------- /ex/string-functions.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.List 3 | import Data.Char 4 | 5 | include :: String -> String -> Bool 6 | include xs ys = or . map (isPrefixOf ys) . tails $ xs 7 | 8 | joinWith :: [String] -> String -> String 9 | joinWith xs sep = concat . init . concat $ [[x, sep] | x <- xs] 10 | 11 | split :: String -> Char -> [String] 12 | split "" _ = [] 13 | split xs c = let (ys, zs) = break (== c) xs 14 | in if null zs then [ys] else ys : split (tail zs) c 15 | 16 | main = do 17 | putStrLn $ "Contains: " ++ show ("test" `include` "es") 18 | putStrLn $ "Count: " ++ show (length . filter (=='t') $ "test") 19 | putStrLn $ "HasPrefix: " ++ show (isPrefixOf "te" "test") 20 | putStrLn $ "HasSuffix: " ++ show (isSuffixOf "st" "test") 21 | putStrLn $ "Index: " ++ show (elemIndex 'e' "test") 22 | putStrLn $ "Join: " ++ show (["a", "b"] `joinWith` "-") 23 | putStrLn $ "Repeat: " ++ show (replicate 5 'a') 24 | putStrLn $ "Replace: " ++ show (map (\x -> if x == 'o' then '0' else x) "foo") 25 | putStrLn $ "Split: " ++ show (split "a-b-c-d-e" '-') 26 | putStrLn $ "ToLower: " ++ map toLower "TEST" 27 | putStrLn $ "ToUpper: " ++ map toUpper "test" 28 | putStrLn "" 29 | putStrLn $ "Len: " ++ show (length "hello") 30 | putStrLn $ "Char:" ++ show ("hello" !! 1) 31 | ``` 32 | 33 | ```bash 34 | $ runhaskell string-functions.hs 35 | Contains: True 36 | Count: 2 37 | HasPrefix: True 38 | HasSuffix: True 39 | Index: Just 1 40 | Join: "a-b" 41 | Repeat: "aaaaa" 42 | Replace: "f00" 43 | Split: ["a","b","c","d","e"] 44 | ToLower: test 45 | ToUpper: TEST 46 | 47 | Len: 5 48 | Char:'e' 49 | ``` 50 | -------------------------------------------------------------------------------- /ex/structs.md: -------------------------------------------------------------------------------- 1 | About Records 2 | 3 | ```haskell 4 | data Person = Person { name :: String 5 | , age :: Int 6 | } deriving Show 7 | 8 | main = do 9 | print $ Person "Bob" 20 10 | print $ Person {name = "Alice", age = 30} 11 | -- print $ Person {name = "Fred"} 12 | print $ Person {name = "Ann", age = 40} 13 | 14 | let s = Person {name = "Sean", age = 50} 15 | putStrLn $ name s 16 | print $ age s 17 | 18 | let s' = s {age = 51} 19 | print $ age s' 20 | ``` 21 | 22 | ```bash 23 | $ runhaskell structs.hs 24 | Person {name = "Bob", age = 20} 25 | Person {name = "Alice", age = 30} 26 | Person {name = "Ann", age = 40} 27 | Sean 28 | 50 29 | 51 30 | ``` 31 | -------------------------------------------------------------------------------- /ex/switch.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.Time 3 | import Data.Time.Calendar.WeekDate 4 | 5 | main = do 6 | let i = 2 7 | putStr $ "write " ++ show i ++ " as " 8 | case i of 9 | 1 -> putStrLn "one" 10 | 2 -> putStrLn "two" 11 | 3 -> putStrLn "three" 12 | 13 | now <- getCurrentTime 14 | let (_, _, week) = toWeekDate . utctDay $ now 15 | putStrLn $ 16 | case week of 17 | 6 -> "it's the weekend" 18 | 7 -> "it's the weekend" 19 | _ -> "it's a weekday" 20 | 21 | localtime <- utcToLocalZonedTime now 22 | let hour = todHour . localTimeOfDay . zonedTimeToLocalTime $ localtime 23 | case hour of 24 | _ 25 | | hour < 12 -> putStrLn "it's before noon" 26 | | otherwise -> putStrLn "it's after noon" 27 | ``` 28 | 29 | ```bash 30 | $ runhaskell switch.hs 31 | write 2 as two 32 | it's a weekday 33 | it's after noon 34 | ``` 35 | -------------------------------------------------------------------------------- /ex/tickers.md: -------------------------------------------------------------------------------- 1 | To work this example, you need install [io-stream](https://hackage.haskell.org/package/io-streams). 2 | 3 | ```bash 4 | $ cabal install io-stream 5 | ``` 6 | 7 | ```haskell 8 | import Control.Concurrent 9 | import Control.Monad.IO.Class 10 | import Data.Time 11 | import Data.IORef 12 | import System.IO.Streams (InputStream, makeOutputStream, fromGenerator, connect) 13 | import qualified System.IO.Streams as Streams 14 | 15 | main = do 16 | ticker <- newTicker (500 * 1000) 17 | output <- makeOutputStream $ \m -> case m of 18 | Just now -> print now 19 | Nothing -> return () 20 | forkIO $ (snd ticker) `connect` output 21 | 22 | threadDelay (1500 * 1000) 23 | stopTicker ticker 24 | putStrLn "Ticker stopped" 25 | 26 | data State = Start | Stop 27 | type Ticker = (IORef State, InputStream UTCTime) 28 | 29 | newTicker :: Int -> IO Ticker 30 | newTicker delay = do 31 | state <- newIORef Start 32 | stream <- fromGenerator (gen state) 33 | return (state, stream) 34 | where 35 | gen state = do 36 | runState <- liftIO $ readIORef state 37 | case runState of 38 | Start -> do 39 | now <- liftIO getCurrentTime 40 | Streams.yield now 41 | liftIO $ threadDelay delay 42 | gen state 43 | Stop -> return () 44 | 45 | stopTicker :: Ticker -> IO () 46 | stopTicker (state, _) = writeIORef state Stop 47 | ``` 48 | 49 | ```bash 50 | $ runhaskell tickers.hs 51 | 2015-05-05 12:30:57.82369 UTC 52 | 2015-05-05 12:30:58.330511 UTC 53 | 2015-05-05 12:30:58.834687 UTC 54 | Ticker stopped 55 | ``` 56 | -------------------------------------------------------------------------------- /ex/time-formatting-parsing.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.Time 3 | import System.Locale 4 | 5 | main = do 6 | let rfc3339like = "%FT%T%z" 7 | 8 | t <- getZonedTime 9 | putStrLn $ formatTime defaultTimeLocale rfc3339like t 10 | 11 | print $ (parseTime defaultTimeLocale rfc3339like "2012-11-01T22:08:41+00:00" :: Maybe ZonedTime) 12 | 13 | putStrLn $ formatTime defaultTimeLocale "%-l:%M%p" t 14 | putStrLn $ formatTime defaultTimeLocale "%a %b %-e %X %Y" t 15 | putStrLn $ formatTime defaultTimeLocale "%FT%T.%q%z" t 16 | 17 | let form = "%-l %M %p" 18 | print $ (parseTime defaultTimeLocale form "8 41 PM" :: Maybe UTCTime) 19 | 20 | let ansic = "%a %b %-e %X %Y" 21 | print $ (parseTime defaultTimeLocale form "8:41PM" :: Maybe UTCTime) 22 | ``` 23 | 24 | ```bash 25 | $ runhaskell time-formatting-parsing.hs 26 | 2015-05-05T22:33:20+0900 27 | Just 2012-11-01 22:08:41 +0000 28 | 10:33PM 29 | Tue May 5 22:33:20 2015 30 | 2015-05-05T22:33:20.122951000000+0900 31 | Just 1970-01-01 20:41:00 UTC 32 | Nothing 33 | ``` 34 | -------------------------------------------------------------------------------- /ex/time.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Data.Time 3 | import Data.Time.Calendar.WeekDate 4 | 5 | main = do 6 | now <- getCurrentTime 7 | print now 8 | 9 | let next = UTCTime { utctDay = fromGregorian 2009 11 17 10 | , utctDayTime = timeOfDayToTime (TimeOfDay 20 34 58.651387237) 11 | } 12 | print next 13 | 14 | let (year, month, day) = toGregorian . utctDay $ next 15 | print year 16 | print month 17 | print day 18 | let hour = todHour . timeToTimeOfDay . utctDayTime $ next 19 | let minute = todMin . timeToTimeOfDay . utctDayTime $ next 20 | let (second, nano) = properFraction . todSec . timeToTimeOfDay . utctDayTime $ next 21 | let (_, _, week) = toWeekDate . utctDay $ next 22 | print hour 23 | print minute 24 | print second 25 | print nano 26 | print week 27 | 28 | print $ next < now 29 | print $ next > now 30 | print $ next == now 31 | ``` 32 | 33 | ```bash 34 | $ runhaskell time.hs 35 | 2015-05-05 13:30:48.204639 UTC 36 | 2009-11-17 20:34:58.651387237 UTC 37 | 2009 38 | 11 39 | 17 40 | 20 41 | 34 42 | 58 43 | 0.651387237000 44 | 2 45 | True 46 | False 47 | False 48 | ``` 49 | -------------------------------------------------------------------------------- /ex/timeouts.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | {-# LANGUAGE GADTs #-} 3 | import Control.Concurrent 4 | import Control.Concurrent.STM 5 | 6 | main = do 7 | c1 <- atomically $ newTQueue 8 | forkIO $ do 9 | threadDelay (2 * 1000000) 10 | atomically $ writeTQueue c1 "result 1" 11 | 12 | t1 <- newTimer (1 * 1000000) 13 | select [ Case c1 $ \res -> putStrLn res 14 | , Case t1 $ \_ -> putStrLn "timeout 1"] 15 | 16 | c2 <- atomically $ newTQueue 17 | forkIO $ do 18 | threadDelay (2 * 1000000) 19 | atomically $ writeTQueue c2 "result 2" 20 | 21 | t2 <- newTimer (3 * 1000000) 22 | select [ Case c2 $ \res -> putStrLn res 23 | , Case t2 $ \_ -> putStrLn "timeout 2"] 24 | 25 | type Timer = TMVar () 26 | 27 | newTimer :: Int -> IO Timer 28 | newTimer delay = do 29 | timer <- atomically newEmptyTMVar 30 | forkIO $ do 31 | threadDelay delay 32 | atomically $ putTMVar timer () 33 | return timer 34 | 35 | class Selectable f where 36 | tryRead :: f a -> STM (Maybe a) 37 | 38 | instance Selectable TMVar where 39 | tryRead = tryReadTMVar 40 | 41 | instance Selectable TQueue where 42 | tryRead = tryReadTQueue 43 | 44 | data Select a where 45 | Default :: IO a -> Select a 46 | Case :: Selectable s => s b -> (b -> IO a) -> Select a 47 | 48 | select :: [Select a] -> IO a 49 | select [] = error "select: empty list" 50 | select ((Default x):_) = x 51 | select (x@(Case v f):xs) = do 52 | var <- atomically $ tryRead v 53 | case var of 54 | Just b -> f b 55 | Nothing -> select (xs ++ [x]) 56 | ``` 57 | 58 | ```bash 59 | $ runhaskell timeouts.hs 60 | timeout 1 61 | result 2 62 | ``` 63 | -------------------------------------------------------------------------------- /ex/timers.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Control.Concurrent 3 | import Control.Concurrent.STM 4 | 5 | main = do 6 | timer1 <- newTimer (2 * 1000000) 7 | waitTimer timer1 8 | putStrLn "Timer 1 expired" 9 | 10 | timer2 <- newTimer (1 * 1000000) 11 | forkIO $ do 12 | waitTimer timer2 13 | putStrLn "Timer 2 expired" 14 | stopTimer timer2 15 | putStrLn "Timer 2 stopped" 16 | 17 | data State = Start | Stop 18 | type Timer = (TVar State, TMVar ()) 19 | 20 | waitTimer :: Timer -> IO () 21 | waitTimer (_, timer) = atomically $ readTMVar timer 22 | 23 | stopTimer :: Timer -> IO () 24 | stopTimer (state, _) = atomically $ writeTVar state Stop 25 | 26 | newTimer :: Int -> IO Timer 27 | newTimer n = do 28 | state <- atomically $ newTVar Start 29 | timer <- atomically $ newEmptyTMVar 30 | forkIO $ do 31 | threadDelay n 32 | atomically $ do 33 | runState <- readTVar state 34 | case runState of 35 | Start -> putTMVar timer () 36 | Stop -> return () 37 | return (state, timer) 38 | ``` 39 | 40 | ```bash 41 | $ runhaskell timers.hs 42 | Timer 1 expired 43 | Timer 2 stopped 44 | ``` 45 | -------------------------------------------------------------------------------- /ex/url-parsing.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Network.URI 3 | 4 | main = do 5 | let s = "postgres://user:pass@host.com:5432/path?k=v#f" 6 | case parseURI s of 7 | Nothing -> error "no URI" 8 | Just uri -> do 9 | putStrLn $ uriScheme uri 10 | case uriAuthority uri of 11 | Nothing -> error "no Authority" 12 | Just auth -> do 13 | putStrLn $ uriUserInfo auth 14 | putStrLn $ uriRegName auth 15 | putStrLn $ uriPort auth 16 | putStrLn $ uriPath uri 17 | putStrLn $ uriFragment uri 18 | putStrLn $ uriQuery uri 19 | ``` 20 | 21 | ```bash 22 | $ runhaskell url-parsing.hs 23 | postgres: 24 | user:pass@ 25 | host.com 26 | :5432 27 | /path 28 | #f 29 | ?k=v 30 | ``` 31 | -------------------------------------------------------------------------------- /ex/values.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | 3 | main = do 4 | putStrLn $ "haskell " ++ "lang" 5 | putStrLn $ "1+1 = " ++ show (1+1) 6 | putStrLn $ "7.0/3.0 = " ++ show (7.0/3.0) 7 | 8 | print $ True && False 9 | print $ True || False 10 | print $ not True 11 | ``` 12 | 13 | ```bash 14 | $ runhaskell values.hs 15 | haskell lang 16 | 1+1 = 2 17 | 7.0/3.0 = 2.3333333333333335 18 | False 19 | True 20 | False 21 | ``` 22 | -------------------------------------------------------------------------------- /ex/variables.md: -------------------------------------------------------------------------------- 1 | see also [Variable](https://wiki.haskell.org/Variable) 2 | 3 | ```haskell 4 | main = do 5 | let a = "initial" 6 | putStrLn a 7 | 8 | let b = 1 9 | let c = 2 10 | print b >> print c 11 | 12 | let d = True 13 | print d 14 | 15 | let e = undefined :: Int 16 | print e 17 | 18 | let f = "short" 19 | putStrLn f 20 | ``` 21 | 22 | ```bash 23 | $ runhaskell variables.hs 24 | initial 25 | 1 26 | 2 27 | True 28 | variables.hs: Prelude.undefined 29 | ``` 30 | -------------------------------------------------------------------------------- /ex/variadic-functions.md: -------------------------------------------------------------------------------- 1 | Use list. 2 | 3 | ```haskell 4 | sum' :: [Int] -> IO () 5 | sum' xs = do 6 | putStr $ show xs ++ " " 7 | print $ sum xs 8 | 9 | main = do 10 | sum' [1, 2] 11 | sum' [1, 2, 3] 12 | 13 | let nums = [1, 2, 3, 4] 14 | sum' nums 15 | ``` 16 | 17 | ```bash 18 | $ runhaskell variadic-functions.hs 19 | [1,2] 3 20 | [1,2,3] 6 21 | [1,2,3,4] 10 22 | ``` 23 | -------------------------------------------------------------------------------- /ex/worker-pools.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Control.Monad 3 | import Control.Concurrent 4 | import Control.Concurrent.STM 5 | 6 | worker :: Int -> TQueue Int -> TQueue Int -> MVar () -> IO () 7 | worker n jobs results lock = forever $ do 8 | j <- atomically $ readTQueue jobs 9 | withMVar lock $ \_ -> do 10 | putStrLn $ "worker " ++ show n ++ " processing job " ++ show j 11 | threadDelay (1 * 1000000) 12 | atomically $ writeTQueue results (2 * j) 13 | 14 | main = do 15 | jobs <- atomically $ newTQueue 16 | results <- atomically $ newTQueue 17 | lock <- newMVar () 18 | 19 | forM_ [1..3] $ \w -> do 20 | forkIO $ worker w jobs results lock 21 | 22 | forM_ [1..9] $ atomically . writeTQueue jobs 23 | forM_ [1..9] $ \_ -> atomically $ readTQueue results 24 | ``` 25 | 26 | ```bash 27 | $ runhaskell worker-pools.hs 28 | worker 1 processing job 1 29 | worker 2 processing job 2 30 | worker 3 processing job 3 31 | worker 2 processing job 4 32 | worker 3 processing job 5 33 | worker 1 processing job 6 34 | worker 3 processing job 7 35 | worker 1 processing job 8 36 | worker 2 processing job 9 37 | ``` 38 | -------------------------------------------------------------------------------- /ex/writing-files.md: -------------------------------------------------------------------------------- 1 | ```haskell 2 | import Foreign.Marshal.Array 3 | import Control.Exception 4 | import Data.Int 5 | import System.IO 6 | 7 | main = do 8 | let d1 = "hello\nhaskell\n" 9 | writeFile "/tmp/dat1" d1 10 | 11 | bracket (openBinaryFile "/tmp/dat2" WriteMode) 12 | (\handle -> hClose handle) 13 | $ \handle -> do 14 | let d2 = [115, 111, 109, 101, 10] :: [Int8] 15 | p <- newArray d2 16 | hPutBuf handle p (length d2) 17 | putStrLn $ "wrote " ++ show (length d2) ++ " bytes" 18 | 19 | hPutStr handle "writes\n" 20 | putStrLn "wrote 7 bytes" 21 | 22 | hSetBuffering handle (BlockBuffering Nothing) -- default 23 | hPutStr handle "buffered\n" 24 | putStrLn "wrote 9 bytes" 25 | hFlush handle 26 | ``` 27 | 28 | ```bash 29 | $ runhaskell writing-files.hs 30 | wrote 5 bytes 31 | wrote 7 bytes 32 | wrote 9 bytes 33 | 34 | $ cat /tmp/dat1 35 | hello 36 | haskell 37 | $ cat /tmp/dat2 38 | some 39 | writes 40 | buffered 41 | ``` 42 | -------------------------------------------------------------------------------- /index.jade: -------------------------------------------------------------------------------- 1 | #intro 2 | h1 Haskell by Example 3 | :markdown 4 | [Haskell](https://www.haskell.org/) is an advanced purely-functional programming language. 5 | 6 | *Haskell by Example* is a port of [*Go by Example*](https://gobyexample.com/) to Haskell. Check out the [first example](ex/hello-world) or browse the full list below. 7 | 8 | ul 9 | for post, slug in public.ex._data 10 | li: a(href="ex/#{slug}") #{post.title} 11 | 12 | :markdown 13 | ```bash 14 | $ diff go-by-example haskell-by-example 15 | -Pointers 16 | -Closing Channels 17 | -Range over Channels 18 | ``` 19 | 20 | by [@lotz84](https://github.com/lotz84) | [source](https://github.com/lotz84/haskellbyexample) | [license](https://github.com/lotz84/haskellbyexample#license) 21 | -------------------------------------------------------------------------------- /js/prism.js: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+clike+bash+haskell */ 2 | self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content),e.alias):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof a)){u.lastIndex=0;var m=u.exec(d);if(m){c&&(f=m[1].length);var y=m.index-1+f,m=m[0].slice(f),v=m.length,k=y+v,b=d.slice(0,y+1),w=d.slice(k+1),N=[p,1];b&&N.push(b);var O=new a(l,g?t.tokenize(m,g):m,h);N.push(O),w&&N.push(w),Array.prototype.splice.apply(r,N)}}}}}return r},hooks:{all:{},add:function(e,n){var a=t.hooks.all;a[e]=a[e]||[],a[e].push(n)},run:function(e,n){var a=t.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(n)}}},n=t.Token=function(e,t,n){this.type=e,this.content=t,this.alias=n};if(n.stringify=function(e,a,r){if("string"==typeof e)return e;if("Array"===t.util.type(e))return e.map(function(t){return n.stringify(t,a,e)}).join("");var i={type:e.type,content:n.stringify(e.content,a,r),tag:"span",classes:["token",e.type],attributes:{},language:a,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===t.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),a=n.language,r=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(r,t.languages[a])))),self.close()},!1),self.Prism):self.Prism;var a=document.getElementsByTagName("script");return a=a[a.length-1],a&&(t.filename=a.src,document.addEventListener&&!a.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; 3 | Prism.languages.markup={comment://,prolog:/<\?.+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/i,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/=|>|"/}},punctuation:/\/?>/,"attr-name":{pattern:/[\w:-]+/,inside:{namespace:/^[\w-]+?:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(t){"entity"===t.type&&(t.attributes.title=t.content.replace(/&/,"&"))});; 4 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0}],string:/("|')(\\\n|\\?.)*?\1/,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(true|false)\b/,"function":{pattern:/[a-z0-9_]+\(/i,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|~|\^|%/,ignore:/&(lt|gt|amp);/i,punctuation:/[{}[\];(),.:]/};; 5 | Prism.languages.bash=Prism.languages.extend("clike",{comment:{pattern:/(^|[^"{\\])(#.*?(\r?\n|$))/,lookbehind:!0},string:{pattern:/("|')(\\?[\s\S])*?\1/,inside:{property:/\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^\}]+\})/}},number:{pattern:/([^\w\.])-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/,lookbehind:!0},"function":/\b(?:alias|apropos|apt-get|aptitude|aspell|awk|basename|bash|bc|bg|builtin|bzip2|cal|cat|cd|cfdisk|chgrp|chmod|chown|chroot|chkconfig|cksum|clear|cmp|comm|command|cp|cron|crontab|csplit|cut|date|dc|dd|ddrescue|declare|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|echo|egrep|eject|enable|env|ethtool|eval|exec|exit|expand|expect|export|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|getopts|git|grep|groupadd|groupdel|groupmod|groups|gzip|hash|head|help|hg|history|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|jobs|join|kill|killall|less|link|ln|locate|logname|logout|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|make|man|mkdir|mkfifo|mkisofs|mknod|more|most|mount|mtools|mtr|mv|mmv|nano|netstat|nice|nl|nohup|notify-send|nslookup|open|op|passwd|paste|pathchk|ping|pkill|popd|pr|printcap|printenv|printf|ps|pushd|pv|pwd|quota|quotacheck|quotactl|ram|rar|rcp|read|readarray|readonly|reboot|rename|renice|remsync|rev|rm|rmdir|rsync|screen|scp|sdiff|sed|select|seq|service|sftp|shift|shopt|shutdown|sleep|slocate|sort|source|split|ssh|stat|strace|su|sudo|sum|suspend|sync|tail|tar|tee|test|time|timeout|times|touch|top|traceroute|trap|tr|tsort|tty|type|ulimit|umask|umount|unalias|uname|unexpand|uniq|units|unrar|unshar|until|uptime|useradd|userdel|usermod|users|uuencode|uudecode|v|vdir|vi|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yes|zip)\b/,keyword:/\b(if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)\b/}),Prism.languages.insertBefore("bash","keyword",{property:/\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^}]+\})/}),Prism.languages.insertBefore("bash","comment",{important:/(^#!\s*\/bin\/bash)|(^#!\s*\/bin\/sh)/});; 6 | Prism.languages.haskell={comment:{pattern:/(^|[^-!#$%*+=\?&@|~.:<>^\\])(--[^-!#$%*+=\?&@|~.:<>^\\].*(\r?\n|$)|{-[\w\W]*?-})/m,lookbehind:!0},"char":/'([^\\"]|\\([abfnrtv\\"'&]|\^[A-Z@[\]\^_]|NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|\d+|o[0-7]+|x[0-9a-fA-F]+))'/,string:/"([^\\"]|\\([abfnrtv\\"'&]|\^[A-Z@[\]\^_]|NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|\d+|o[0-7]+|x[0-9a-fA-F]+)|\\\s+\\)*"/,keyword:/\b(case|class|data|deriving|do|else|if|in|infixl|infixr|instance|let|module|newtype|of|primitive|then|type|where)\b/,import_statement:{pattern:/(\n|^)\s*(import)\s+(qualified\s+)?(([A-Z][_a-zA-Z0-9']*)(\.[A-Z][_a-zA-Z0-9']*)*)(\s+(as)\s+(([A-Z][_a-zA-Z0-9']*)(\.[A-Z][_a-zA-Z0-9']*)*))?(\s+hiding\b)?/m,inside:{keyword:/\b(import|qualified|as|hiding)\b/}},builtin:/\b(abs|acos|acosh|all|and|any|appendFile|approxRational|asTypeOf|asin|asinh|atan|atan2|atanh|basicIORun|break|catch|ceiling|chr|compare|concat|concatMap|const|cos|cosh|curry|cycle|decodeFloat|denominator|digitToInt|div|divMod|drop|dropWhile|either|elem|encodeFloat|enumFrom|enumFromThen|enumFromThenTo|enumFromTo|error|even|exp|exponent|fail|filter|flip|floatDigits|floatRadix|floatRange|floor|fmap|foldl|foldl1|foldr|foldr1|fromDouble|fromEnum|fromInt|fromInteger|fromIntegral|fromRational|fst|gcd|getChar|getContents|getLine|group|head|id|inRange|index|init|intToDigit|interact|ioError|isAlpha|isAlphaNum|isAscii|isControl|isDenormalized|isDigit|isHexDigit|isIEEE|isInfinite|isLower|isNaN|isNegativeZero|isOctDigit|isPrint|isSpace|isUpper|iterate|last|lcm|length|lex|lexDigits|lexLitChar|lines|log|logBase|lookup|map|mapM|mapM_|max|maxBound|maximum|maybe|min|minBound|minimum|mod|negate|not|notElem|null|numerator|odd|or|ord|otherwise|pack|pi|pred|primExitWith|print|product|properFraction|putChar|putStr|putStrLn|quot|quotRem|range|rangeSize|read|readDec|readFile|readFloat|readHex|readIO|readInt|readList|readLitChar|readLn|readOct|readParen|readSigned|reads|readsPrec|realToFrac|recip|rem|repeat|replicate|return|reverse|round|scaleFloat|scanl|scanl1|scanr|scanr1|seq|sequence|sequence_|show|showChar|showInt|showList|showLitChar|showParen|showSigned|showString|shows|showsPrec|significand|signum|sin|sinh|snd|sort|span|splitAt|sqrt|subtract|succ|sum|tail|take|takeWhile|tan|tanh|threadToIOResult|toEnum|toInt|toInteger|toLower|toRational|toUpper|truncate|uncurry|undefined|unlines|until|unwords|unzip|unzip3|userError|words|writeFile|zip|zip3|zipWith|zipWith3)\b/,number:/\b(\d+(\.\d+)?([eE][+-]?\d+)?|0[Oo][0-7]+|0[Xx][0-9a-fA-F]+)\b/,operator:/\s\.\s|([-!#$%*+=\?&@|~:<>^\\]*\.[-!#$%*+=\?&@|~:<>^\\]+)|([-!#$%*+=\?&@|~:<>^\\]+\.[-!#$%*+=\?&@|~:<>^\\]*)|[-!#$%*+=\?&@|~:<>^\\]+|(`([A-Z][_a-zA-Z0-9']*\.)*[_a-z][_a-zA-Z0-9']*`)/,hvariable:/\b([A-Z][_a-zA-Z0-9']*\.)*[_a-z][_a-zA-Z0-9']*\b/,constant:/\b([A-Z][_a-zA-Z0-9']*\.)*[A-Z][_a-zA-Z0-9']*\b/,punctuation:/[{}[\];(),.:]/};; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "harp": "^0.30.0" 4 | }, 5 | "scripts": { 6 | "start": "harp server" 7 | } 8 | } 9 | --------------------------------------------------------------------------------