├── .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+""+i.tag+">"},!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 |
--------------------------------------------------------------------------------