├── LICENSE
├── write_haskell_as_fast_as_c.md
├── README.md
└── dialogues.md
/LICENSE:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
--------------------------------------------------------------------------------
/write_haskell_as_fast_as_c.md:
--------------------------------------------------------------------------------
1 | # Write Haskell as fast as C: exploiting strictness, laziness and recursion
2 |
3 | - 2008-05-16
4 |
5 | - by Don Stewart (dons)
6 |
7 | - Originally posted at "Haskell Hacking: a journal of Haskell programming"
8 |
9 | In a recent mailing list thread Andrew Coppin complained of poor performance with "nice, declarative" code for computing the mean of a very large list of double precision floating point values:
10 |
11 | ```haskell
12 | import System.Environment
13 | import Text.Printf
14 |
15 | mean :: [Double] -> Double
16 | mean xs = sum xs / fromIntegral (length xs)
17 |
18 | main = do
19 | [d] <- map read `fmap` getArgs
20 | printf "%f\n" (mean [1 .. d])
21 | ```
22 |
23 | Which, when executed, churns away for a while, but eventually runs out of memory:
24 |
25 | ```bash
26 | $ ghc -O2 --make A.hs
27 | $ time ./A 1e9
28 | A: out of memory (requested 1048576 bytes)
29 | ./A 1e9 12.74s user 1.20s system 99% cpu 13.997 total
30 | ```
31 |
32 | Well, it got 13 seconds into the job doing something. At least it was making progress.
33 |
34 | While Andrew was citing this as an example of GHC Haskell being unpredictable (it's not an example of that, as we'll see), there is something here -- this little program reveals a lot about the interaction between strictness, laziness and approaches to compiler optimisation in Haskell.
35 |
36 | This post is about writing reliably fast code, and then how to write more naive code that is also reliably fast. In particular, how recursion consistently produces excellent code, competitive with heavily optimised C, and how to get similar results from higher order functions. Throughout this post I'll be using GHC 6.8.2, with -O2. Sometimes I'll switch to the C backend (-fvia-C -optc-O2). Also, I don't care about smarter ways to implement this -- we're simply interested in understanding the compiler transformations involved in the actual loops involved.
37 |
38 | Understanding strictness and laziness
39 |
40 | First, let's work out why this ran out of memory. The obvious hard constraint is that we request a list of 1e9 doubles: that is very, very large. It's 8 * 10^9 bytes, if allocated directly, or about 7.5G. Inside a list there is yet further overhead, for the list nodes, and their pointers. So no matter the language, we simply cannot allocate this all in one go without burning gigabytes of memory. The only approach that will work is to lazily generate the list somehow. To confirm this, we can use a strict list data type, just to see how far we get.
41 |
42 | First, let's define a strict list data type. That is one whose head and tail are always fully evaluated. In Haskell, strictness is declared by putting bangs on the fields of the data structure, though it can also be inferred based on how values are used:
43 |
44 | ```haskell
45 | data List a = Empty
46 | | Cons !a !(List a)
47 | ```
48 |
49 | This is a new data type, so we'll need some new functions on it:
50 |
51 | ```haskell
52 | length' :: List a -> Int
53 | length' = go 0
54 | where
55 | go n Empty = n
56 | go n (Cons _ xs) = go (n+1) xs
57 |
58 | sum' :: Num a => List a -> a
59 | sum' xs = go 0 xs
60 | where
61 | go n Empty = n
62 | go n (Cons x xs) = go (n+x) xs
63 |
64 | enumFromTo' :: (Num a, Ord a) => a -> a -> List a
65 | enumFromTo' x y | x > y = Empty
66 | | otherwise = go x
67 | where
68 | go x = Cons x (if x == y then Empty else go (x+1))
69 | ```
70 |
71 | I've written these in a direct worker/wrapper transformed, recursive style. It's a simple, functional idiom that's guaranteed to get the job done.
72 |
73 | So now we can compute the length and sum of these things, and generate one given a range. Our 'mean' function stays much the same, just the type changes from a lazy to strict list:
74 |
75 | ```haskell
76 | mean :: List Double -> Double
77 | mean xs = sum' xs / fromIntegral (length' xs)
78 | ```
79 |
80 | Testing this, we see it works fine for small examples:
81 |
82 | ```haskell
83 | $ time ./B 1000
84 | 500.5
85 | ./A 1000 0.00s user 0.00s system 0% cpu 0.004 total
86 | ```
87 |
88 | But with bigger examples, it quickly exhausts memory:
89 |
90 | ```haskell
91 | $ time ./B 1e9
92 | Stack space overflow: current size 8388608 bytes.
93 | Use `+RTS -Ksize' to increase it.
94 | ./A 1e9 0.02s user 0.02s system 79% cpu 0.050 tota
95 | ```
96 |
97 | To see that we can't even get as far as summing the list, we'll replace the list body with undefined, and just ask for the list argument to be evaluated before the body with a bang patterns (a strictness hint to the compiler, much like a type annotation suggests the desired type):
98 |
99 | ```haskell
100 | mean :: List Double -> Double
101 | mean !xs = undefined
102 |
103 | main = do
104 | [d] <- map read `fmap` getArgs
105 | printf "%f\n" (mean (enumFromTo' 1 d))
106 | ```
107 |
108 | And still, there's no hope to allocate that thing:
109 |
110 | ```bash
111 | $ time ./A 1e9
112 | Stack space overflow: current size 8388608 bytes.
113 | Use `+RTS -Ksize' to increase it.
114 | ./A 1e9 0.01s user 0.04s system 93% cpu 0.054 total
115 | ```
116 |
117 | Strictness is not the solution here.
118 |
119 | Traversing structures and garbage collection
120 |
121 | So why is the naive version actually allocating the whole list anyway? It was a lazy list, shouldn't it somehow avoid allocating the elements? To understand why it didn't work, we can look at what:
122 |
123 | ```haskell
124 | mean :: [Double] -> Double
125 | mean xs = sum xs / fromIntegral (length xs)
126 | ```
127 |
128 | actually means. To reason about the code, one way is to just look at the final, optimised Haskell GHC produces, prior to translation to imperative code. This reduced Haskell is known as "core", and is the end result of GHC's optimisations-by-transformation process, which iteratively rewrites the original source into more and more optimised versions.
129 |
130 | Referring to the core is useful if you're looking for C-like performance, as you can state precisely, to the register level, what your program will do -- there are no optimisations to perform that would confuse the interpretation. It is an entirely unambiguous form of Haskell, so we'll use it to clarify any uncertainties. (For everyday programming, a comfortable knowledge of laziness and strictness is entirely adequate, so don't panic if you don't read core!).
131 |
132 | To view the core, we can use ghc -O2 -ddump-simpl, or the ghc-core tool. Looking at it for the naive program we see that 'mean' is translated to:
133 |
134 | ```haskell
135 | $wlen :: [Double] -> Int# -> Int#
136 | ...
137 |
138 | $wsum'1 :: [Double] -> Double# -> Double#
139 | ...
140 |
141 | main = ...
142 | shows (
143 | let xs :: [Double]
144 | xs = enumFromTo1 lvl3 d_a6h
145 | in
146 | case $wsum'1 xs 0.0 of {
147 | _ -> case $wlen xs 0 of {
148 | z -> case ww_s19a /## (int2Double# z) of {
149 | i -> D# i
150 | )
151 | ```
152 |
153 | It looks like a lot of funky pseudo-Haskell, but its made up of very simple primitives with a precise meaning. First, length and sum are specialised -- their polymorphic components replaced with concrete types, and in this case, those types are simple, atomic ones, so Double and Int are replaced with unlifted (or unboxed) values. That's good to know -- unlifted values can be kept in registers.
154 |
155 | We can also confirm that the input list is allocated lazily.
156 |
157 | ```haskell
158 | let xs :: [Double]
159 | xs = enumFromTo1 lvl3 d_a6h
160 | in ..
161 | ```
162 |
163 | The 'let' here is the lazy allocation primitive. If you see it on non-unboxed type, you can be sure that thing will be lazily allocated on the heap. Remeber: core is like Haskell, but there's only 'let' and 'case' (one for laziness, one for evaluation).
164 |
165 | So that's good. It means the list itself isn't costing us anything to write. This lazy allocation also explains why we're able to make some progress before running out of memory resources -- the list is only allocated as we traverse it.
166 |
167 | However, all is not perfect, the next lines reveal:
168 |
169 | ```haskell
170 | case $wsum'1 xs 0.0 of {
171 | _ -> case $wlen xs 0 of {
172 | z -> case ww_s19a /## (int2Double# z) of {
173 | i -> D# i
174 | ```
175 |
176 | Now, 'case' is the evaluation primitive. It forces evaluation to the outermost constructor of the scrutinee -- the expression we're casing on -- right where you see it.
177 |
178 | In this way we get an ordering of operations set up by the compiler. In our naive program, first the sum of the list is performed, then the length of the list is computed, until finally we divide the sum by the length.
179 |
180 | That sounds fine, until we think about what happened to our lazily allocated list. The initial 'let' does no work, when allocating. However, the first 'sum' will traverse the entire list, computing the sum of its elements. To do this it must evaluate the entire huge list.
181 |
182 | Now, on its own, that is still OK -- the garbage collector will race along behind 'sum', deallocating list nodes that aren't needed anymore. However, there is a fatal flaw in this case. 'length' still refers to the head of the list. So the garbage collector cannot release the list: it is forced to keep the whole thing alive.
183 |
184 | 'sum', then, will allocate the huge list, and it won't be collected till after 'length' has started consuming it -- which is too late. And this is exactly as we observed. The original poster's unpredictably is an entirely predictable result if you try to traverse a 7G lazy list twice.
185 |
186 | Note that this would be even worse if we'd been in a strict language: the initial 'let' would have forced evaluation, so you'd get nowhere at all.
187 |
188 | Now we have enough information to solve the problem: make sure we make only one traversal of the huge list. so we don't need to hang onto it.
189 |
190 | Lazy lists are OK
191 |
192 | The simple thing to do then, and the standard functional approach for the last 40 years of functional programming is to write a loop to do both sum and length at once:
193 |
194 | ```haskell
195 | mean :: [Double] -> Double
196 | mean = go 0 0
197 | where
198 | go :: Double -> Int -> [Double] -> Double
199 | go s l [] = s / fromIntegral l
200 | go s l (x:xs) = go (s+x) (l+1) xs
201 | ```
202 |
203 | We just store the length and sum in the function parameters, dividing for the mean once we reach the end of the list. In this way we make only one pass. Simple, straight forward. Should be the standard approach in the Haskell hackers kit.
204 |
205 | By writing it in an obvious recursive style that walks the list as it is created, we can be sure to run in constant space. Our code compiles down to:
206 |
207 | ```haskell
208 | $wgo :: Double# -> Int# -> [Double] -> Double#
209 | $wgo x y z =
210 | case z of
211 | [] -> /## x (int2Double# y);
212 | x_a9X : xs_a9Y ->
213 | case x_a9X of
214 | D# y_aED -> $wgo (+## x y_aED) (+# y 1) xs_a9Y
215 | ```
216 |
217 | We see that it inspects the list, determining if it is an empty list, or one with elements. If empty, return the division result. If non-empty, inspect the head of the list, taking the raw double value out of its box, then loop, On a small list:
218 |
219 | ```bash
220 | $ time ./B 1000
221 | 500.5
222 | ./B 1000 0.00s user 0.00s system 0% cpu 0.004 total
223 | ```
224 |
225 | On the 7 gigabyte list:
226 |
227 | ```bash
228 | $ time ./B 1e9
229 | 500000000.067109
230 | ./B 1e9 68.92s user 0.19s system 99% cpu 1:09.57 total
231 | ```
232 |
233 | Hooray, we've computed the result! The lazy list got us home.
234 |
235 | But can we do better? Looking at the GC and memory statistics (run the program with +RTS -sstderr ) we can see how much work was going on:
236 |
237 | ```
238 | 24,576 bytes maximum residency (1 sample(s))
239 |
240 | %GC time 1.4% (4.0% elapsed)
241 | Alloc rate 2,474,068,932 bytes per MUT second
242 | Productivity 98.6% of total user
243 | ```
244 |
245 | What does this tell us? Firstly, garbage collection made up a tiny fraction of the total time (1.4%), meaning the program was doing productive thing 98.6% of the time -- that's very good.
246 |
247 | Also, we can see it ran in constant space: 24k bytes maximum allocated in the heap. And the program was writing and releasing these list nodes at an alarming rate. 2G a second (stunning, I know). Good thing allocation is cheap.
248 |
249 | However, this does suggest that we can do better -- much better -- by avoiding any list node allocation at all and keeping off the bus. We just need to prevent list nodes from being allocated at all -- by keeping only the current list value in a register -- if we can stay out of memory, we should see dramatic improvements.
250 |
251 | Recursion kicks arse
252 |
253 | So let's rewrite the loop to no longer take a list, but instead, the start and end values of the loop as arguments:
254 |
255 | ```haskell
256 | mean :: Double -> Double -> Double
257 | mean n m = go 0 0 n
258 | where
259 | go :: Double -> Int -> Double -> Double
260 | go s l x | x > m = s / fromIntegral l
261 | | otherwise = go (s+x) (l+1) (x+1)
262 |
263 | main = do
264 | [d] <- map read `fmap` getArgs
265 | printf "%f\n" (mean 1 d)
266 | ```
267 |
268 | We've moved the list lower and upper bounds into arguments to the function. Now there are no lists whatsover. This is a fusion transformation, instead of two separate loops, one for generation of the list, and one consuming it, we've fused them into a single loop with all operations interleaved. This should now avoid the penalty of the list memory traffic. We've also annotated the types of the functions, so there can be no ambiguity about what machine types are used.
269 |
270 | Looking at the core:
271 |
272 | ```haskell
273 | $wgo_s17J :: Double# -> Int# -> Double# -> Double#
274 | $wgo_s17J x y z =
275 | case >## z m of
276 | False ->
277 | $wgo_s17J
278 | (+## x z)
279 | (+# y 1)
280 | (+## z 1.0);
281 | True -> /## x (int2Double# y)
282 | ```
283 |
284 | it is remarkably simple. All parameters are unboxed, the loop is tail recursive, and we can expect zero garbage collection, all list parameters in registers, and we'd expect excellent performance. The >## and +## are GHC primitives for double comparison and addition. +# is normal int addition.
285 |
286 | Let's compile it with GHC's native code generator first (-O2 -fexcess-precision):
287 |
288 | ```bash
289 | $ time ./C 1e9
290 | 500000000.067109
291 | ./C 1e9 3.75s user 0.00s system 99% cpu 3.764 total
292 | ```
293 |
294 | Great. Very good performance! The memory statistics tell a similar rosy story:
295 |
296 | 20,480 bytes maximum residency (1 sample(s))
297 | %GC time 0.0% (0.0% elapsed)
298 | Alloc rate 10,975 bytes per MUT second
299 | Productivity 100.0% of total user
300 | So it ran in constant space, did no garbage collection, and was allocating only a few bytes a second. Recursion is the breakfast of champions.
301 |
302 | And if we use the C backend to GHC: (-O2 -fexcess-precision -fvia-C -optc-O2), things get seriously fast:
303 |
304 | ```bash
305 | $ time ./C 1e9
306 | 500000000.067109
307 | ./C 1e9 1.77s user 0.00s system 99% cpu 1.776 total
308 | ```
309 |
310 | Wow, much faster. GCC was able to really optimise the loop GHC produced. Good job.
311 |
312 | Let's see if we can get this kind of performance in C itself. We can translate the recursive loop directly into a for loop, where function arguments becomes the variables in the loop:
313 |
314 | ```c
315 | #include
316 | #include
317 |
318 | int main(int argc, char **argv) {
319 |
320 | double d = atof(argv[1]);
321 |
322 | double n;
323 | long long a; // 64 bit machine
324 | double b;
325 |
326 | // go_s17J :: Double# -> Int# -> Double# -> Double#
327 | for (n = 1,
328 | a = 0,
329 | b = 0; n <= d; b+=n,
330 | n++,
331 | a++)
332 | ;
333 |
334 | printf("%f\n", b / a);
335 |
336 | return 0;
337 | }
338 | ```
339 |
340 | That was quite straight forward, and the C is nicely concise. It is interesting to see how function parameters are updated in place explicitly in the C code, while its implicitly reusing stack slots (or registers) in the functional style. But they're really describing the same code. Now, compiling the C without optimisations:
341 |
342 | ```bash
343 | $ time ./a.out 1e9
344 | 500000000.067109
345 | ./a.out 1e9 4.72s user 0.00s system 99% cpu 4.723 total
346 | ```
347 |
348 | Ok. That's cool. We got the same results, and optimised Haskell beat unoptimsed C. Now with -O, gcc is getting closer to catch GHC:
349 |
350 | ```bash
351 | $ time ./a.out 1e9
352 | 500000000.067109
353 | ./a.out 1e9 2.10s user 0.00s system 99% cpu 2.103 total
354 | ```
355 |
356 | And with -O2 it makes it in front by a nose:
357 |
358 | ```bash
359 | $ time ./a.out 1e9
360 | 500000000.067109
361 | ./a.out 1e9 1.76s user 0.00s system 99% cpu 1.764 total
362 | ```
363 |
364 | gcc -O2 wins the day (barely?), just sneaking past GHC -O2, which comes in ahead of gcc -O and -Onot.
365 |
366 | We can look at the generated assembly (ghc -keep-tmp-files) to find out what's really going on.. GCC generates the rather nice:
367 |
368 | ```asm
369 | .L6:
370 | addsd %xmm1, %xmm2
371 | incq %rax
372 | addsd %xmm3, %xmm1
373 | ucomisd %xmm1, %xmm0
374 | jae .L6
375 |
376 | .L8:
377 | cvtsi2sdq %rax, %xmm0
378 | movl $.LC2, %edi
379 | movl $1, %eax
380 | divsd %xmm0, %xmm2
381 | ```
382 |
383 | Note the very tight inner loop, and final division. Meanwhile, GHC produces, with its native code backend:
384 |
385 | ```asm
386 | s1bn_info:
387 | ucomisd 5(%rbx),%xmm6
388 | ja .Lc1dz
389 | movsd %xmm6,%xmm0
390 | addsd .Ln1dB(%rip),%xmm0
391 | leaq 1(%rsi),%rax
392 | addsd %xmm6,%xmm5
393 | movq %rax,%rsi
394 | movsd %xmm0,%xmm6
395 | jmp s1bn_info
396 |
397 | .Lc1dz:
398 | cvtsi2sdq %rsi,%xmm0
399 | divsd %xmm0,%xmm5
400 | ```
401 |
402 | Quite a bit more junk in the inner loop, which explains the slowdown with -fasm. That native code gen needs more work for competitve floating point work. But with the GHC C backend:
403 |
404 | ```asm
405 | s1bn_info:
406 | ucomisd 5(%rbx), %xmm6
407 | ja .L10
408 | addsd %xmm6, %xmm5
409 | addq $1, %rsi
410 | addsd .LC1(%rip), %xmm6
411 | jmp s1bn_info
412 |
413 | .L10:
414 | cvtsi2sdq %rsi, %xmm7
415 | divsd %xmm7, %xmm5
416 | ```
417 |
418 | Almost identical to GCC, which explains why the performance was so good!
419 |
420 | Some lessons
421 |
422 | Lesson 1: To write predictably fast Haskell -- the kind that competes with C day in and out -- use tail recursion, and ensure all types are inferred as simple machine types, like Int, Word, Float or Double that simple machine representations. The performance is there if you want it.
423 |
424 | Lesson 2: Laziness has an overhead -- while it allows you to write new kinds of programs (where lists may be used as control structures), the memory traffic that results can be a penalty if it appears in tight inner loops. Don't rely laziness to give you performance in your inner loops.
425 |
426 | Lesson 3: For heavy optimisation, the C backend to GHC is still the way to go. Later this year a new bleeding edge native code generator will be added to GHC, but until then, the C backend is still an awesome weapon.
427 |
428 | In a later post we'll examine how to get the same performance by using higher order functions.
429 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is my recommended path for learning Haskell.
2 |
3 | #### Something to keep in mind: *don't sweat the stuff you don't understand immediately*. Just keep moving.
4 |
5 | # Community
6 |
7 | Our IRC channel is `#haskell-beginners` on Freenode.
8 |
9 | IRC web client here: http://webchat.freenode.net/
10 |
11 | ## Community Guidelines
12 |
13 | [Letter to a Young Haskell Enthusiast](http://comonad.com/reader/2014/letter-to-a-young-haskell-enthusiast/)
14 |
15 | Be nice above all else!
16 |
17 | # Primary course
18 |
19 | ## What are Haskell, GHC, and Cabal?
20 |
21 | Haskell is a programming language as laid out in the reports, most recent one being in 2010. http://www.haskell.org/onlinereport/haskell2010/
22 |
23 | GHC is the most popular compiler for Haskell and is what you'll install along with Cabal. Cabal is the project and dependency management tool used with GHC. You almost definitely want both if you're going to start writing Haskell.
24 |
25 | Cabal is equivalent to Ruby's Bundler, Python's pip, Node's NPM, Maven, etc. GHC manages packaging itself, Cabal chooses what versions to install.
26 |
27 | ## Getting started
28 |
29 | ### Ubuntu
30 |
31 | This PPA is excellent and is what I use on all my Linux dev and build machines: http://launchpad.net/~hvr/+archive/ghc
32 |
33 | Specifically:
34 |
35 | - `sudo apt-get update`
36 | - **12.04 and below** -> `sudo apt-get install python-software-properties`
37 | - **12.04 and above** -> `sudo apt-get install software-properties-common`
38 | - `sudo add-apt-repository -y ppa:hvr/ghc`
39 | - `sudo apt-get update`
40 | - `sudo apt-get install cabal-install-1.20 ghc-7.8.3 happy-1.19.4 alex-3.1.3`
41 |
42 | Then add `~/.cabal/bin:/opt/cabal/1.20/bin:/opt/ghc/7.8.3/bin:/opt/happy/1.19.4/bin:/opt/alex/3.1.3/bin` to your PATH (bash_profile, zshrc, bashrc, etc)
43 |
44 | *Optional* You could also add `.cabal-sandbox/bin` to your path. Code that you are actively developing will be available to you from the command line.
45 | This only works when your current working directory is a cabal sandbox.
46 |
47 | ### Debian
48 |
49 | #### GHC Repository for debian stable
50 | If you use debian stable, it is easier to use this: http://deb.haskell.org/. After installing GHC:
51 | - Get `cabal-install` source at https://www.haskell.org/cabal/download.html
52 | - Run `bootstrap.sh`
53 | - Run `cabal update && cabal install cabal cabal-install alex happy`
54 |
55 | #### Using Ubuntu PPA
56 | If you're not using stable, you can follow the same steps as Ubuntu, but has to execute an additional command. Immediately after `sudo add-apt-repository -y ppa:hvr/ghc` is ran, run:
57 |
58 | - `sudo sed -i s/wheezy/trusty/g /etc/apt/sources.list.d/hvr-ghc-wheezy.list`
59 |
60 | For other Debian versions, just replace all occurences of "wheezy" with your version name in the command above.
61 |
62 | If, for some reason, the file `/etc/apt/sources.list.d/hvr-ghc-wheezy.list` does not exist, try the same command but with `/etc/apt/sources.list` instead.
63 |
64 | #### Manual compilation
65 | You can follow the guide written for Mac OS X: http://www.davesquared.net/2014/05/platformless-haskell.html. Notes:
66 | - set your prefix accordingly when configuring ghc
67 | - instead of grabbing the `cabal-install` binary, grab the source and then run `bootstrap.sh` script.
68 |
69 | ### Fedora 20
70 |
71 | To install Haskell 7.8x from the unofficial repo (Fedora 21+ will include it in the official one:
72 | - Add https://copr.fedoraproject.org/coprs/petersen/ghc-7.8/repo/fedora-20/petersen-ghc-7.8-fedora-20.repo as petersen-ghc-7.8-fedora-20.repo
73 | - `sudo yum install ghc`
74 |
75 | ### Arch Linux
76 |
77 | To install Haskell from the official repos on Arch Linux, run
78 |
79 | su -c "pacman -S cabal-install ghc happy alex haddock"
80 |
81 | ### Mac OS X
82 |
83 | Install the GHC for Mac OS X app, which includes GHC and Cabal. It provides instructions on how to add GHC and Cabal to your path after you've dropped the .app somewhere.
84 |
85 | - http://ghcformacosx.github.io/
86 |
87 | ### Windows and other Linux users
88 |
89 | Download the latest binary distributions for cabal and ghc:
90 |
91 | #### GHC
92 |
93 | GHC is the most popular way to work in the Haskell language. It includes a compiler, REPL (interpreter), package management, and other things besides.
94 |
95 | - http://www.haskell.org/ghc/
96 |
97 | #### Cabal
98 |
99 | Cabal does project management and dependency resolution. It's how you'll install projects, typically into their own sandbox.
100 |
101 | - https://www.haskell.org/cabal/download.html
102 |
103 | #### Detailed manual install guide for Mac OS X
104 |
105 | You don't need this if you use the .app, but if it doesn't work for you, try this with the binary distribution.
106 |
107 | - http://www.davesquared.net/2014/05/platformless-haskell.html
108 |
109 | ---
110 |
111 | ## Yorgey course - *Do this first*, this is the primary way I recommend being introduced to Haskell.
112 |
113 |
114 | http://www.seas.upenn.edu/~cis194/spring13/index.html Brent Yorgey's course is the best I've found so far and replaces both Yann Esposito's HF&H. This course is valuable as it will not only equip you to write basic Haskell but also help you to understand parser combinators.
115 |
116 | The only reason you shouldn't start with cis194 is if you are not a programmer or are an inexperienced one. If that's the case, start with http://learnyouahaskell.com/ and transition to cis194.
117 |
118 | Learn You A Haskell and http://book.realworldhaskell.org/ are recommended primarily as supplemental references for completing the cis194 course if you are not new to programming. RWH has some additional material that LYAH does not that is useful to people using Haskell in production as well.
119 |
120 | ---
121 |
122 | ## Supplementary course that provides more material on intermediate topics
123 |
124 | This is Bryan O'Sullivan's online course from the class he teaches at Stanford. If you don't know who he is, take a gander at half the libraries any Haskell application ends up needing and his name is on it. Of particular note if you've already done the Yorgey course are the modules on phantom types, information flow control, language extensions, concurrency, pipes, and lenses.
125 |
126 | - http://www.scs.stanford.edu/14sp-cs240h/
127 |
128 | ## Exercises for practice (the NICTA course)
129 |
130 | You should do Yorgey's course before attempting this: https://github.com/NICTA/course/
131 |
132 | ## Secondary material, references
133 |
134 | [Learn You a Haskell for Great Good (LYAH)](http://learnyouahaskell.com) and [Real World Haskell](http://book.realworldhaskell.org) (Thanks bos!) are available online.
135 |
136 | I recommend RWH as a reference (thick book). The chapters for parsing and monads are great for getting a sense for where monads are useful. Other people have said that they've liked it a lot. Perhaps a good follow-up for practical idioms after you've got the essentials of Haskell down?
137 |
138 | ### For learning some common typeclasses
139 |
140 | Useful for understanding `Functor`, `Applicative`, `Monad`, `Monoid` and other typeclasses in general but also some Hask-specific category theory:
141 | - http://www.haskell.org/haskellwiki/Typeclassopedia
142 |
143 | ### Understanding basic Haskell error messages
144 |
145 | - http://ics.p.lodz.pl/~stolarek/_media/pl:research:stolarek_understanding_basic_haskell_error_messages.pdf
146 |
147 | ## Development Environment
148 |
149 | ### Emacs
150 |
151 | - https://github.com/serras/emacs-haskell-tutorial/blob/master/tutorial.md
152 |
153 | - https://github.com/bitemyapp/dotfiles/
154 |
155 | ### Vim
156 |
157 | - http://www.haskell.org/haskellwiki/Vim
158 |
159 | - http://www.stephendiehl.com/posts/vim_haskell.html
160 |
161 | - https://github.com/kazu-yamamoto/ghc-mod
162 |
163 | - https://github.com/eagletmt/ghcmod-vim
164 |
165 | ### Sublime Text
166 |
167 | - https://github.com/SublimeHaskell/SublimeHaskell
168 |
169 | ## FAQ and working with Cabal
170 |
171 | ### Fantastic FAQ
172 |
173 | In addition to being an amazing guide for all kinds of things such as GADTs, this also covers some useful basics for Cabal
174 |
175 | - http://dev.stephendiehl.com/hask/
176 |
177 | ### Cabal guidelines
178 |
179 | - http://softwaresimply.blogspot.com/2014/07/haskell-best-practices-for-avoiding.html
180 |
181 | Cabal Hell was a problem for Haskell users before the introduction of sandboxes. Installing outside of a sandbox will install into your user package-db. This is *not* a good idea except for foundational packages like Cabal, alex, and happy. Nothing else should be installed in the user or global package-dbs unless you know what you're doing.
182 |
183 | To experiment with a package or start a project, begin by doing `cabal sandbox init` in a new directory.
184 |
185 | Put briefly:
186 |
187 | - Always use sandboxes for installing new packages, building new or existing projects, or starting experiments
188 | - Use `cabal repl` to start a project-scoped ghci instance
189 |
190 | The sandbox-based approach I suggest should avoid package-dependency problems, but it's incompatible with the way the Haskell Platform provides pre-built packages. If you're still learning Haskell and don't understand how ghc-pkg and Cabal work, *avoid Platform* and instead use the install instructions earlier in the guide.
191 |
192 | ## Search code by type signature
193 |
194 | The Hoogle search engine can search by type:
195 |
196 | http://www.haskell.org/hoogle/?hoogle=%28a+-%3E+b%29+-%3E+%5ba%5d+-%3E+%5bb%5d
197 |
198 | Alternately:
199 |
200 | https://www.fpcomplete.com/hoogle
201 |
202 | Also Hayoo (which has all of hackage enabled for search by default): http://holumbus.fh-wedel.de/hayoo/hayoo.html
203 |
204 | ### Setting up your own local instance of Hoogle
205 |
206 | https://gist.github.com/bitemyapp/3e6a015760775e0679bf
207 |
208 | ## Haddock
209 |
210 | - First: http://fuuzetsu.co.uk/blog/posts/2014-01-06-Fix-your-Hackage-documentation.html
211 | - Second: http://fuuzetsu.co.uk/blog/posts/2014-01-06-Hackage-documentation-v2.html
212 |
213 | ### What you really need to know
214 |
215 | In order to have haddocks include documentation for related packages, you have to set documentation: True in your ~/.cabal/config. If it was left on the default (False) or set to False, you'll have to delete all your packages and reinstall before generating haddocks.
216 |
217 | The other thing to keep in mind is that due to the way the $pkg parameter gets interpolated *by* haddock, not by you, the html-location and content-location parameters must be in single quotes and entered into a shell or contained in a shell script. They will not work in a Makefile, because it will think they are Make variables!
218 |
219 | ```bash
220 | #!/bin/bash
221 |
222 | cabal haddock --hoogle --hyperlink-source --html-location='http://hackage.haskell.org/package/$pkg/docs' --contents-location='http://hackage.haskell.org/package/$pkg'
223 | ```
224 |
225 | ## Fun Stuff
226 |
227 | After you're comfortable with Haskell, strongly consider learning Lenses and Prisms, even if just as a "user". You don't need to understand the underlying category for it to be useful.
228 |
229 | Seen here: http://hackage.haskell.org/package/lens
230 |
231 | ## Frontend/JavaScript
232 |
233 | If you need JavaScript, you probably want Purescript for generating JS. Purescript is *not* strictly Haskell but it is very similar and quite pleasant.
234 |
235 | - http://www.purescript.org/
236 |
237 | - http://try.purescript.org/
238 |
239 | - http://www.christopherbiscardi.com/2014/06/22/getting-started-with-purescript/ Great guide for getting started
240 |
241 | ## Parsing and generating JSON
242 |
243 | - http://blog.raynes.me/blog/2012/11/27/easy-json-parsing-in-haskell-with-aeson/
244 |
245 | - http://bitemyapp.com/posts/2014-04-11-aeson-and-user-created-types.html
246 |
247 | - http://bitemyapp.com/posts/2014-04-17-parsing-nondeterministic-data-with-aeson-and-sum-types.html
248 |
249 | - https://www.fpcomplete.com/school/starting-with-haskell/libraries-and-frameworks/text-manipulation/json
250 |
251 | ## Laziness, strictness, guarded recursion
252 |
253 | - http://chimera.labs.oreilly.com/books/1230000000929/ch02.html Marlow's book about parallelism and concurrency has one of the best introductions to laziness and normal form I've found. Use other material too if it doesn't stick immediately.
254 |
255 | - http://augustss.blogspot.hu/2011/05/more-points-for-lazy-evaluation-in.html
256 |
257 | - http://alpmestan.com/2013/10/02/oh-my-laziness/
258 |
259 | - http://stackoverflow.com/questions/13042353/does-haskell-have-tail-recursive-optimization
260 |
261 | - http://www.slideshare.net/tibbe/reasoning-about-laziness
262 |
263 | ### For a more thorough understanding of laziness, NF, WHNF
264 |
265 | - https://vec.io/posts/notes-on-lambda-calculus
266 |
267 | - http://homepages.inf.ed.ac.uk/wadler/topics/call-by-need.html#need-journal
268 |
269 | - http://www.itu.dk/~sestoft/papers/sestoft-lamreduce.pdf
270 |
271 | - http://www.cs.ox.ac.uk/files/293/lazy.pdf
272 |
273 | ### Brief demonstration
274 |
275 | ```haskell
276 | let a = 1 : a -- guarded recursion, (:) is lazy and can be pattern matched.
277 | let (v : _) = a
278 | > v
279 | 1
280 | > head a -- head a == v
281 | 1
282 |
283 | let a = 1 * a -- not guarded, (*) is strict
284 | > a
285 | *** Exception: <>
286 |
287 | ```
288 |
289 | - http://www.vex.net/~trebla/haskell/lazy.xhtml
290 |
291 | ## Parallelism/Concurrency
292 |
293 | - http://chimera.labs.oreilly.com/books/1230000000929 This book by Simon Marlow is probably the best I've ever read on the topics of Parallelism and Concurrency:
294 |
295 | - http://kukuruku.co/hub/haskell/haskell-testing-a-multithread-application A thorough walk-through on testing & incremental development of a multi-threaded application in Haskell
296 |
297 | - http://www.haskell.org/haskellwiki/Functional_Reactive_Programming
298 |
299 | ## Lenses and Prisms
300 |
301 | People vastly overestimate the difficulty of using Lens. Anybody comfortable with Functor/Foldable/Traversable (or even just the first one) can leverage lenses and prisms to make their life happier.
302 |
303 | If you've ever done something like: `(fmap . fmap)` you were "lensing" in your head.
304 |
305 | I recommend these two tutorials/introductions:
306 |
307 | - https://www.fpcomplete.com/school/to-infinity-and-beyond/pick-of-the-week/a-little-lens-starter-tutorial
308 |
309 | - https://github.com/ekmett/lens#lens-lenses-folds-and-traversals
310 |
311 | ## Monads and monad transformers
312 |
313 | ### Do not do these until you understand typeclasses, Monoid, Functor, and Applicative!
314 |
315 | Implement the standard library monads ( List, Maybe, Cont, Error, Reader, Writer, State ) for yourself to understand them better. Then maybe write an monadic interpreter for a small expression language using Monad Transformers Step by Step paper.
316 |
317 | Writing many interpreters by just changing the monad to change the semantics can help convey what's going on.
318 |
319 | - http://www.cs.virginia.edu/~wh5a/personal/Transformers.pdf
320 |
321 | Also, reimplement Control.Monad. Functions like `mapM` or `sequence` are good opportunities to practice writing generic monadic code.
322 |
323 | The NICTA course can be used as a guide to this process, which will also involve writing your own Applicative as well.
324 |
325 | From:
326 | - http://www.reddit.com/r/haskell/comments/29eke6/basic_program_ideas_for_learning_about_monads/cik5aj6
327 | - http://www.reddit.com/r/haskell/comments/29eke6/basic_program_ideas_for_learning_about_monads/cik5trg
328 |
329 | ### Monad transformers
330 |
331 | - https://github.com/kqr/gists/blob/master/articles/gentle-introduction-monad-transformers.md
332 |
333 | - https://vimeo.com/73648150
334 |
335 | ## Resource handling, finalization, cleanup
336 |
337 | - Overview of resourceT by Snoyman: https://www.fpcomplete.com/user/snoyberg/library-documentation/resourcet
338 |
339 | ## Streaming IO
340 |
341 | - https://github.com/jwiegley/simple-conduit Good simple library for learning how streaming IO works in general, knowledge transferrable to libraries like Pipes and Conduit
342 |
343 | ## Recursion Schemes
344 |
345 | Some of the crazy *-morphism words you've heard are actually about recursion. NB - before tackling this material you should know how to implement foldr for lists and at least one other data structure, such as a tree. (folds are catamorphisms) Knowing how to implement an unfold (anamorphism) for the same will round things out a bit.
346 |
347 | This material dovetails with traversable and foldable.
348 |
349 | - http://patrickthomson.ghost.io/an-introduction-to-recursion-schemes/
350 |
351 | - http://fho.f12n.de/posts/2014-05-07-dont-fear-the-cat.html - good demonstration of how hylomorphism is the composition of cata and ana.
352 |
353 | - http://comonad.com/reader/2009/recursion-schemes/ - this field guide is excellent.
354 |
355 | - http://eprints.eemcs.utwente.nl/7281/01/db-utwente-40501F46.pdf
356 |
357 | - https://www.fpcomplete.com/user/edwardk/recursion-schemes/catamorphisms
358 |
359 | ## Type and Category Theory (*not* needed to actually write Haskell, just for those interested!)
360 |
361 | If you want to follow up on the type and category theory:
362 |
363 | - http://byorgey.wordpress.com/2014/01/14/catsters-guide/ and http://byorgey.wordpress.com/catsters-guide-2/
364 |
365 | - http://www.cs.cmu.edu/~rwh/plbook/book.pdf Harper's Practical Foundations for Programming Languages is the best PL focused intro to type theory I've read.
366 |
367 | - http://www.quora.com/Category-Theory/What-is-the-best-textbook-for-Category-theory?share=1 Kmett's recommendations
368 |
369 | - http://en.wikibooks.org/wiki/Haskell/Category_theory nice diagrams
370 |
371 | - http://www.haskell.org/haskellwiki/Category_theory good links to other resources
372 |
373 | - http://science.raphael.poss.name/categories-from-scratch.html includes practical examples
374 |
375 | - https://www.google.com/search?q=Awodey+Category+Theory the standard text along with MacLane
376 |
377 | - http://www.cs.kent.ac.uk/people/staff/sjt/TTFP/
378 |
379 | - http://www.cis.upenn.edu/~bcpierce/courses/670Fall04/GreatWorksInPL.shtml Pierce's Great Works in PL list
380 |
381 | ### Stephen's Nifty "How to get to monad" posts
382 |
383 | Didn't know where else to put these:
384 |
385 | - http://www.stephendiehl.com/posts/adjunctions.html
386 |
387 | - http://www.stephendiehl.com/posts/monads.html
388 |
389 | ## Parametricity, ad-hoc vs. parametric polymorphism, free theorems
390 |
391 | - http://dl.dropboxusercontent.com/u/7810909/media/doc/parametricity.pdf
392 |
393 | - https://github.com/tonymorris/parametricity/
394 |
395 | - http://swizec.com/blog/week-20-making-ad-hoc-polymorphism-less-ad-hoc/swizec/6564
396 |
397 | - http://ttic.uchicago.edu/~dreyer/course/papers/wadler.pdf
398 |
399 | ## Initial and Final, DSLs, Finally Tagless
400 |
401 | - http://creativelad.wordpress.com/2013/11/28/final-encodings-part-1-a-quick-demonstration/
402 |
403 | - http://martijn.van.steenbergen.nl/journal/2009/10/18/transforming-polymorphic-values/
404 |
405 | - http://martijn.van.steenbergen.nl/journal/2009/11/12/gadts-in-haskell-98/
406 |
407 | - https://www.fpcomplete.com/user/mutjida/typed-tagless-final-linear-lambda-calculus
408 |
409 | - http://okmij.org/ftp/tagless-final/course/course.html
410 |
411 | - http://okmij.org/ftp/tagless-final/course/lecture.pdf
412 |
413 | - http://existentialtype.wordpress.com/2011/03/21/the-dog-that-didnt-bark/ less specifically relevant but interesting
414 |
415 | ## Comonads
416 |
417 | - https://speakerdeck.com/dmoverton/comonads-in-haskell
418 |
419 | - http://stackoverflow.com/questions/16551734/can-a-monad-be-a-comonad
420 |
421 | ## Yoneda / CoYoneda
422 |
423 | - http://stackoverflow.com/questions/24000465/step-by-step-deep-explain-the-power-of-coyoneda-preferably-in-scala-throu
424 |
425 | - http://comonad.com/reader/2011/free-monads-for-less/
426 |
427 | - http://comonad.com/reader/2011/free-monads-for-less-2/
428 |
429 | - http://comonad.com/reader/2011/free-monads-for-less-3/
430 |
431 | ## IO
432 |
433 | - https://www.fpcomplete.com/user/snoyberg/general-haskell/advanced/evaluation-order-and-state-tokens
434 |
435 | Comment from Reddit thread by `glaebhoerl`
436 |
437 |
438 | Interesting side note: GHC needs to hide the state token representation behind an abstract IO type because the state token must always be used linearly (not duplicated or dropped), but the type system can't enforce this. Clean, another lazy Haskell-like language, has uniqueness types (which are like linear types and possibly different in ways I'm not aware of), and they expose the World-passing directly and provide a (non-abstract) IO monad only for convenience.
439 |
440 | - http://blog.ezyang.com/2011/05/unraveling-the-mystery-of-the-io-monad/
441 |
442 | - http://blog.jle.im/entry/first-class-statements
443 |
444 | - http://hackage.haskell.org/package/base-4.7.0.1/docs/System-IO-Unsafe.html#v:unsafePerformIO Read the docs and note implementation of unsafeDupablePerformIO
445 |
446 | ## GHC Core and performance tuning
447 |
448 | - [Write Haskell as Fast as C](write_haskell_as_fast_as_c.md)
449 |
450 | - https://ghc.haskell.org/trac/ghc/wiki/Commentary/Compiler/CoreSynType
451 |
452 | - https://hackage.haskell.org/package/ghc-core
453 |
454 | - http://stackoverflow.com/questions/6121146/reading-ghc-core
455 |
456 | - http://donsbot.wordpress.com/2008/06/04/haskell-as-fast-as-c-working-at-a-high-altitude-for-low-level-performance/
457 |
458 | - http://book.realworldhaskell.org/read/profiling-and-optimization.html
459 |
460 | ## Dependent typing
461 |
462 | - http://bitemyapp.com/posts/2014-04-05-grokking-sums-and-constructors.html squint hard.
463 |
464 | - http://okmij.org/ftp/Computation/lightweight-dependent-typing.html
465 |
466 | - http://www.idris-lang.org/
467 |
468 | ## Propositions vs. Judgments (computation)
469 |
470 | - http://cstheory.stackexchange.com/questions/9826/what-is-the-difference-between-propositions-and-judgments
471 |
472 | - http://www.ae-info.org/attach/User/Martin-L%C3%B6f_Per/OtherInformation/article.pdf
473 |
474 | ## Extended Reading list (some is already included here)
475 |
476 | - http://www.stephendiehl.com/posts/essential_haskell.html
477 |
478 | ## Dialogues
479 |
480 | [Dialogues](dialogues.md) these are actually pretty important and
481 | helpful. Look here for deep dives on a variety of topics.
482 |
--------------------------------------------------------------------------------
/dialogues.md:
--------------------------------------------------------------------------------
1 | # Dialogues from the IRC channel or other places
2 |
3 | ## State monad vs. fold
4 |
5 | Martin:
6 |
7 | Hello all,
8 |
9 | many times I see a problem and I say to myself: "there is some state". I then play around with the state monad and often
10 | I don't get anywhere. Then at some point I realizes, that all I need is a simple fold. I don't think I ever used the
11 | state monad outside of toy examples.
12 |
13 | Can someone give me some insights when the State Monad is beneficial and where a fold is the better choice.
14 |
15 | * * * * *
16 |
17 | John Wiegley:
18 |
19 | >>>>> martin writes:
20 |
21 | > Can someone give me some insights when the State Monad is beneficial and
22 | > where a fold is the better choice.
23 |
24 | Looking at the type of a fold:
25 |
26 | ```haskell
27 | foldr :: (a -> b -> b) -> b -> [a] -> b
28 | ```
29 |
30 | If we juggle the arguments we get:
31 |
32 | ```haskell
33 | foldr :: (a -> b -> b) -> [a] -> b -> b
34 | ```
35 |
36 | And if we imagine State b () actions, we can directly rewrite this as:
37 |
38 | ```haskell
39 | foldrS :: (a -> State b ()) -> [a] -> State b ()
40 | ```
41 |
42 | Which generalizes to:
43 |
44 | ```haskell
45 | foldrS :: MonadState b m => (a -> m ()) -> [a] -> m ()
46 | ```
47 |
48 | Which is roughly the same thing as using mapM_ over our State monad:
49 |
50 | ```haskell
51 | mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
52 | ```
53 |
54 | In other words, these two forms in our example say the same thing:
55 |
56 | ```haskell
57 | foldr f b xs
58 | execState (mapM_ f' xs) b
59 | ```
60 |
61 | With the only difference being the types of f and f':
62 |
63 | ```haskell
64 | f : a -> b -> b
65 | f' : a -> State b ()
66 | ```
67 |
68 | The other question you asked is when to choose one over the other. Since they
69 | are equivalent, it's really up to you. I tend to prefer using a fold over
70 | State, to keep things on the level of functions and values, rather than
71 | pulling in monads and possibly monad transformers unnecessarily.
72 |
73 | But it's a very good thing to hold these isomorphisms in your mind, since you
74 | can then freely switch from one representation to another as need be. This is
75 | true of a lot of type equivalences throughout Haskell, where the more things
76 | you can see as being essentially the same, the more freedom you have to find
77 | the best abstraction for a particular context.
78 |
79 | ## On $ and . operator
80 |
81 | ```haskell
82 | doubleEveryOther :: [Integer] -> [Integer]
83 | doubleEveryOther list = reverse .doubleEveryOtherForward . reverse $ list
84 | ```
85 |
86 | ```
87 | 03:28 < bitemyapp> fbernier: reverse the list, double every other number, re-reverse the list.
88 | 03:28 < bitemyapp> fbernier: the "dot" operator is just function composition.
89 | 03:28 < bitemyapp> it's nothing special, just another function.
90 | 03:28 < bitemyapp> :t (.)
91 | 03:28 < lambdabot> (b -> c) -> (a -> b) -> a -> c
92 | 03:30 < bitemyapp> fbernier: the use of $ in that function is a little idiosyncratic and unnecessary, but not problematic.
93 | 03:37 < ReinH> fbernier: there's a missing space after the . is all
94 | 03:38 < ReinH> fbernier: f x = foo $ x ==> f = foo
95 | 03:39 < ReinH> so f x = foo . bar $ x ==> f = foo . bar
96 | 03:39 < bitemyapp> fbernier: I think it's just making it point-free in this case.
97 | 03:39 < bitemyapp> @pl f x = c . b . a $ x
98 | 03:39 < lambdabot> f = c . b . a
99 | 03:39 < bitemyapp> yeah, that ^^
100 | 03:39 < bitemyapp> fbernier: identical ^^
101 | 03:40 < ReinH> fbernier: generally, when you see a $ you can wrap the things on either side with parens and get the same expression:
102 | 03:40 < ReinH> f x = foo . bar . bazz $ x ==> f x = (foo . bar . bazz) x
103 | 03:40 < ReinH> since (x) = x, ofc
104 | 03:41 < bitemyapp> @src ($)
105 | 03:41 < lambdabot> f $ x = f x
106 | 03:41 < bitemyapp> fbernier: That's the definition of $, only other thing missing is the high precedence set for it.
107 | 03:41 < ReinH> the exception is chains of $, like foo $ bar $ baz, where you have to parenthesize in the right direction
108 | 03:41 < ReinH> or the left direction, depending on how you look at it
109 | 03:42 < bitemyapp> fbernier: http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html ctrl-f for $ to see more
110 | 03:42 < bitemyapp> fbernier: infixr 0 is the precedence, highest there is AFAIK
111 | 03:42 < bitemyapp> fbernier: the "infixr" means it's right associative
112 | 03:42 < bitemyapp> fbernier: as opposed to infixl which would mean left associative
113 | 03:43 < ReinH> bitemyapp: or lowest, depending on how you look at it. ;)
114 | 03:43 < bitemyapp> foo $ bar $ baz ~ foo (bar (baz))
115 | 03:43 < bitemyapp> but if it was infixl
116 | 03:43 < bitemyapp> (((foo) bar) baz)
117 | ```
118 |
119 | ## Infix operators as prefix
120 |
121 | ```
122 | 04:12 < ReinH> all infix operators can be written prefix
123 | 04:12 < ReinH> with this one weird trick. Other haskellers hate him.
124 | 04:13 < bitemyapp> > ($) id 1
125 | 04:13 < lambdabot> 1
126 | 04:13 < bitemyapp> > id $ 1
127 | 04:13 < lambdabot> 1
128 | 04:13 < bitemyapp> > id 1
129 | 04:13 < lambdabot> 1
130 | ```
131 |
132 | ## Reduction, strict evaluation, ASTs, fold, reduce
133 |
134 | ```
135 | 05:00 < ReinH> pyro-: well, "reduce" already has a typeclass, depending on what you mean
136 | 05:00 < ReinH> so does "evaluation", depending on what you mean
137 | 05:02 < pyro-> ReinH: reduce is lambda calculus under strict evaluation
138 | 05:02 < ReinH> Yep, and it's also the other thing too.
139 | 05:02 < ReinH> ;)
140 | 05:03 < pyro-> :|
141 | 05:03 < pyro-> oh, like on lists?
142 | 05:04 < mm_freak_> dealing with ASTs is a real joy in haskell, because most of the code writes itself =)
143 | ```
144 |
145 | ## Continuation passing style, CPS transform
146 |
147 | ```
148 | 05:10 < pyro-> now i am writing a cpsTransform function :D
149 | 05:10 < pyro-> it already works, but the current version introduces superflous continuations
150 | 05:10 < pyro-> so i am trying to fix :D
151 | 05:10 < ReinH> pyro-: Here's a CPS transform function: flip ($)
152 | 05:11 < pyro-> i will find out about flip
153 | 05:11 < ReinH> @src flip
154 | 05:11 < lambdabot> flip f x y = f y x
155 | 05:11 < ReinH> pyro-: the essence of CPS can be described as follows:
156 | 05:11 < ReinH> :t flip ($)
157 | 05:11 < lambdabot> b -> (b -> c) -> c
158 | 05:12 < ReinH> is the type of a function which takes a value and produces a suspended computation that takes a continuation and runs it against the value
159 | 05:12 < ReinH> for example:
160 | 05:12 < ReinH> > let c = flip ($) 3 in c show
161 | 05:12 < lambdabot> "3"
162 | 05:12 < ReinH> > let c = flip ($) 3 in c succ
163 | 05:12 < lambdabot> 4
164 | 05:13 < mm_freak_> direct style: f x = 3*x + 1
165 | 05:13 < mm_freak_> CPS: f x k = k (3*x + 1)
166 | 05:13 < mm_freak_> the rules are: take a continuation argument and be fully polymorphic on the result type
167 | 05:13 < mm_freak_> f :: Integer -> (Integer -> r) -> r
168 | 05:14 < mm_freak_> as long as your result type is fully polymorphic and doesn't unify with anything else in the type signature you can't do anything wrong other than to descend
169 | into an infinite recursion =)
170 | 05:14 < mm_freak_> good: (Integer -> r) -> r
171 | 05:15 < mm_freak_> bad: (Integer -> String) -> String
172 | 05:15 < mm_freak_> bad: (Num r) => (Integer -> r) -> r
173 | 05:15 < mm_freak_> bad: r -> (Integer -> r) -> r
174 | 05:15 < pyro-> but flip ($) is not what i had in mind :D
175 | 05:16 < mm_freak_> that's just one CPS transform… there are many others =)
176 | 05:16 < ReinH> No, it's probably not.
177 | 05:16 < ReinH> But other things are pretty much generalizations of that
178 | ```
179 |
180 | ```haskell
181 | type Variable = String
182 |
183 | data Expression = Reference Variable
184 | | Lambda Variable Expression
185 | | Combination Expression Expression
186 |
187 | type Kvariable = String
188 |
189 | data Uatom = Procedure Variable Kvariable Call
190 | | Ureference Variable
191 |
192 | data Katom = Continuation Variable Call
193 | | Kreference Variable
194 | | Absorb
195 |
196 | data Call = Application Uatom Uatom Katom
197 | | Invocation Katom Uatom
198 |
199 | cpsTransform :: Expression -> Katom -> Call
200 | cpsTransform (Reference r) k = Invocation k $ Ureference r
201 | cpsTransform (Lambda p b) k = Invocation k $ Procedure p
202 | "k" $
203 | cpsTransform b $ Kreference "k"
204 | cpsTransform (Combination a b) k = cpsTransform a $ Continuation "v" $ cpsTransform b k
205 | ```
206 |
207 | ### Later...
208 |
209 | ```
210 | 05:38 < ReinH> So for example, if you have an incredibly simple expression language like data Expr a = Val a | Neg a | Add a a
211 | 05:38 < ReinH> a (more) initial encoding of an expression would be Add (Val 1) (Neg (Val 1))
212 | 05:38 < ReinH> A (more) final encoding might be (1 - 1) or even 0
213 | 05:39 < ReinH> The initial encoding generally is more flexible (you can still write a double-negation elimination rule, for instance
214 | 05:39 < ReinH> the final encoding is less flexible, but also does more work up-front
215 | 05:40 < ReinH> More initial encodings tend to force you to use quantification and type-level tricks, CPS and pre-applied functions tend to appear more in final encodings
216 | 05:40 < ReinH> An even smaller example:
217 | 05:40 < ReinH> \f z -> foldr f z [1,2,3] is a final encoding of the list [1,2,3]
218 | 05:41 < ReinH> pyro-: I'm not really a lisper, but I'm always looking for good reading material
219 | 05:41 < ReinH> for bonus points, the foldr encoding is *invertible* as well :)
220 | 05:44 < ReinH> pyro-: the relevance is that you seem to be using the cps transform in a more initial encoding than I usually see it
221 | 05:44 < ReinH> not that this is at all bad
222 | 05:46 < bitemyapp> ReinH: where does the invertibility in the final encoding come from?
223 | 05:46 < ReinH> foldr (:) [] :)
224 | 05:46 < ReinH> it's not generally so
225 | 05:46 < bitemyapp> > foldr (:) [] [1, 2, 3]
226 | 05:46 < lambdabot> [1,2,3]
227 | 05:47 < bitemyapp> I may not understand the proper meaning of invertibility in this case.
228 | 05:47 < bitemyapp> Do you mean invertibility from final to initial encoding?
229 | 05:47 < ReinH> Just that, yes
230 | 05:47 < bitemyapp> how would it get you back to final from initial?
231 | 05:47 < ReinH> I'm not sure if that's the correct term
232 | 05:47 < bitemyapp> I don't think it is, but the intent is understood and appreciated.
233 | 05:48 < bitemyapp> invertibility implies isomorphism, implies ability to go final -> initial -> final
234 | 05:48 < ReinH> well, there is an isomorphism
235 | 05:48 < bitemyapp> well, we've established final -> initial, where's initial -> final for this example?
236 | 05:49 < bitemyapp> I figured it was a morphism of some sort, but with only a final -> initial and not a way to get back, I wasn't sure which.
237 | 05:49 < ReinH> toInitial k = k (:) []; toFinal xs = \f z -> foldr f z xs
238 | 05:49 < bitemyapp> thank you :)
239 | ```
240 |
241 | ### Something about adjunctions. I don't know.
242 |
243 | ```
244 | 05:51 < ReinH> bitemyapp: usually one loses information going from initial to final though
245 | 05:51 < ReinH> there's probably an adjunction here
246 | 05:51 < ReinH> there's always an adjunction
247 | 05:52 < ReinH> lol of course there's an adjunction
248 | ```
249 |
250 | ## Data structures with efficient head and tail manipulation
251 |
252 | Asker:
253 |
254 | I am teaching myself haskell. The first impression is very good.
255 | But phrase "haskell is polynomially reducible" is making me sad :(.
256 | Anyway I am trying to backport my algorithm written in C. The key to
257 | performance is to have ability to remove element from the end of a
258 | list in O(1).
259 | But the original haskell functions last and init are O(n).
260 | My questions are:
261 | 1) Is last function is something like "black box" written in C++ which
262 | perform O(1)?
263 | So I shouldn't even try to imagine some haskell O(1) equivalent.
264 | 2) Or will optimizer (llvm?) reduce init&last complexity to 1?
265 | 3) Some people suggest to use sequences package, but still how do they
266 | implement O(1) init&last sequences equivalent in haskell?
267 |
268 | * * * * *
269 |
270 | Tom Ellis:
271 |
272 | I'm rather confused about your question. If you want a Haskell data
273 | structure that supports O(1) head, tail, init and last why not indeed use
274 | Data.Sequence as has been suggested? As for how it's implemented, it uses
275 | the (very cool) fingertree datastructure. See here for more details:
276 |
277 | * * * * *
278 |
279 | Asker:
280 |
281 | Tom said that finger tree gives us O(1) on removing last element, but
282 | in haskell all data is persistent.
283 | So function should return list as is minus last element. How it could
284 | be O(1)? This is just blows my mind...
285 |
286 | My hypothesis is that somehow compiler reduces creating of a new list
287 | to just adding or removing one element. If it is not so.
288 | Then even ':' which is just adding to list head would be an O(n)
289 | operation just because it should return brand new list with one elem
290 | added. Or maybe functional approach uses pretty much different
291 | complexity metric, there copying of some structure "list" for example
292 | is just O(1)? If so then Q about compiler is still exists.
293 |
294 | * * * * *
295 |
296 | Tom Ellis:
297 |
298 | Sounds like magic doesn't it :)
299 |
300 | But no, there's no compiler magic, just an amazing datastructure. The
301 | caveat is that the complexity is amortised, not guaranteed for every
302 | operation. Have a look at the paper if you learn about how it works. It's
303 | linked from the Hackage docs.
304 |
305 | http://hackage.haskell.org/package/containers-0.2.0.1/docs/Data-Sequence.html
306 |
307 |
308 | * * * * *
309 |
310 | Asker:
311 |
312 | Jake It would be great if you give some examples when find your
313 | notebook :) And link to the book about pure functional data structures
314 | which you are talking about.
315 | Also If some "haskell.org" maintainers are here I'd like to recommend
316 | them to pay more attention to optimality/performance questions.
317 | Because almost first question which is apeared in head of standart
318 | C/C++ programmer is "Do I get same perfomance?" (even if he do not
319 | need it).
320 | Maybe some simple and cool PDF tutorial which describes why haskell
321 | could be as fast as others will be great to have.
322 |
323 | * * * * *
324 |
325 | Richard A. O'Keefe:
326 |
327 |
328 | > I am teaching myself haskell. The first impression is very good...
329 | > Anyway I am trying to backport my algorithm written in C. The key to
330 | > performance is to have ability to remove element from the end of a
331 | > list in O(1).
332 |
333 | You can't. Not in *any* programming language. That's because
334 | lists are one of many possible implementations of the "sequence"
335 | concept, and they are optimised to support some operations at
336 | the expense of others. At the beginning level, you should think
337 | of all Haskell data structures as immutable; fixed; frozen;
338 | forever unchanged. You can't even remove an element from the
339 | front of a Haskell list, at all. All you can do is to forget
340 | about the original list and concentrate on its tail.
341 |
342 | > But the original haskell functions last and init are O(n).
343 |
344 | Haskell lists are singly linked lists. Even by going to
345 | assembly code, you could not make these operations O(1)
346 | without *using a different data structure*.
347 |
348 | > My questions are:
349 | > 1) Is last function is something like "black box" written in C++ which
350 | > perform O(1)?
351 |
352 | No.
353 |
354 | > 2) Or will optimizer (llvm?) reduce init&last complexity to 1?
355 |
356 | No.
357 |
358 | > 3) Some people suggest to use sequences package, but still how do they
359 | > implement O(1) init&last sequences equivalent in haskell?
360 |
361 | Well, you could try reading Chris Okasaki's functional data
362 | structures book.
363 |
364 | There is a classic queue representation devised for Lisp
365 | last century which represents
366 |
367 | by ([a,b],[e,d,c])
368 | so that you can push and pop at either end.
369 | When the end you are working on runs out, you
370 | reverse the other end, e.g.,
371 | ([],[e,d,c]) -> ([c,d,e],[]).
372 |
373 | That can give you a queue with *amortised* constant time.
374 | (There is a technical issue which I'll avoid for now.)
375 |
376 | But let's start at the beginning.
377 | You have an interesting problem, P.
378 | You have an algorithm for it, A, written in C.
379 | You want an algorithm for it, H, written in Haskell.
380 | Your idea is to make small local syntactic changes
381 | to A to turn in into H.
382 | That's probably going to fail, because C just
383 | loves to smash things, and Haskell hates to.
384 | Maybe you should be using quite a different approach,
385 | one that would be literally unthinkable in C.
386 | After all, being able to do things that are unthinkable
387 | in C is one of the reasons for learning Haskell.
388 |
389 | Why not tell us what problem P is?
390 |
391 | * * * * *
392 |
393 | Tony Morris:
394 |
395 | data SnocList a = SnocList ([a] -> [a])
396 |
397 | Inserts to the front and end in O(1).
398 |
399 | ### I consider the following conclusive
400 |
401 | Edward Kmett:
402 |
403 | Note: all of the options for playing with lists and queues and fingertrees come with trade-offs.
404 |
405 | Finger trees give you O(log n) appends and random access, O(1) cons/uncons/snoc/unsnoc etc. but _cost you_ infinite lists.
406 |
407 | Realtime queues give you the O(1) uncons/snoc. There are catenable output restricted deques that can preserve those and can upgrade you to O(1) append, but we've lost unsnoc and random access along the way.
408 |
409 | Skew binary random access lists give you O(log n) drop and random access and O(1) cons/uncons, but lose the infinite lists, etc.
410 |
411 | Tarjan and Mihaescu's deque may get you back worst-case bounds on more of the, but we still lose O(log n) random access and infinite lists.
412 |
413 | Difference lists give you an O(1) append, but alternating between inspection and construction can hit your asymptotics.
414 |
415 | Lists are used by default because they cleanly extend to the infinite cases, anything more clever necessarily loses some of that power.
416 |
417 | ## listen in Writer monad
418 |
419 | ```
420 | 20:26 < ifesdjee_> hey guys, could anyone point me to the place where I could read up on how `listen` of writer monad works?
421 | 20:26 < ifesdjee_> can't understand it from type signature, don't really know wether it does what i want..
422 | 20:30 < ReinH> :t listen
423 | 20:30 < lambdabot> MonadWriter w m => m a -> m (a, w)
424 | 20:31 < mm_freak_> ifesdjee_: try this: runWriterT (listen (tell "abc" >> tell "def") >>= liftIO . putStrLn . snd)
425 | 20:33 < mm_freak_> in any case 'listen' really just embeds a writer action and gives you access to what it produced
426 | 20:33 < ifesdjee_> most likely i misunderstood what happens in `listen`...
427 | 20:34 < ifesdjee_> i thought i could access current "state" of writer
428 | 20:34 < mm_freak_> remember that the embedded writer's log still becomes part of the overall log
429 | 20:34 < mm_freak_> execWriter (listen (tell "abc") >> tell "def") = "abcdef"
430 | 20:35 < mm_freak_> all you get is access to that "abc" from within the writer action
431 | 20:35 < ifesdjee_> yup, I see
432 | 20:35 < ifesdjee_> thank you a lot!
433 | 20:35 < mm_freak_> my pleasure
434 | 20:37 < mm_freak_> i wonder why there is no evalWriter*
435 | 20:37 < ifesdjee_> not sure, really
436 | ```
437 |
438 | ## Introduction and origination of free monads
439 |
440 | ```
441 | 21:32 < sclv> does anyone have a citation for the introduction of free monads?
442 | 21:33 < sclv> they’re so universally used in the literature nobody cites where they came from anymore
443 | 21:33 < sclv> in a computational context goes back to ’91 at least
444 | 21:40 < sclv> found it
445 | 21:40 < sclv> coequalizers and free triples, barr, 1970
446 | ```
447 |
448 | http://link.springer.com/article/10.1007%2FBF01111838#page-1
449 |
450 | Note: Seeing a paper on free monoids dating to 1972 by Eduardo J. Dubuc.
451 |
452 | ## Rank 2 types and type inference
453 |
454 | ```
455 | 03:13 < shachaf> dolio: Do you know what people mean when they say rank-2 types are inferrable?
456 | 03:14 < dolio> Not really. I've never taken the time to understand it.
457 | 03:16 < dolio> One reading makes no sense, I think. Because rank-2 is sufficient to lack principal types, isn't it?
458 | 03:17 < dolio> Or perhaps it isn't....
459 | 03:17 < shachaf> Well, you can encode existentials.
460 | 03:17 < dolio> Can you?
461 | 03:17 < dolio> forall r. (forall a. a -> r) -> r
462 | 03:17 < dolio> I guess that's rank-2.
463 | 03:18 < shachaf> You can give rank-2 types to expressions like (\x -> x x)
464 | 03:18 < shachaf> What type do you pick for x?
465 | 03:19 < dolio> forall a. a -> β
466 | 03:19 < dolio> Presumably.
467 | 03:20 < shachaf> Does β mean something special here?
468 | 03:20 < dolio> It's still open.
469 | 03:20 < dolio> Greek for unification variables.
470 | 03:21 < shachaf> OK, but what type do you infer for the whole thing?03:21 < dolio> forall r. (forall a. a -> r) -> r
471 | 03:23 < dolio> (\f -> f 6) : forall r. (Int -> r) -> r
472 | 03:23 < dolio> Is that a principal type?
473 | 03:23 < shachaf> Do you allow type classes?
474 | 03:24 < dolio> People who say rank-2 is decidable certainly shouldn't be thinking about type classes.
475 | 03:24 < shachaf> I guess with impredicativity the type you gave works... Well, does it?
476 | 03:25 < dolio> Maybe rank-2 is sufficient to eliminate all ambiguities.
477 | 03:25 < dolio> Like, one common example is: [id]
478 | 03:25 < dolio> Is that forall a. [a -> a] or [forall a. a -> a]
479 | 03:25 < dolio> But, we're not talking about Haskell, we're talking about something like system f.
480 | 03:26 < dolio> So you'd have to encode.
481 | 03:26 < dolio> And: (forall r. ((forall a. a -> a) -> r -> r) -> r -> r) is rank-3.
482 | 03:27 < shachaf> I guess...
483 | 03:27 < dolio> If I had to guess, that's what the answer is.
484 | ```
485 |
486 | - Practical type inference for arbitrary-rank types - Peyton Jones, Vytinotis, Weirich, Shields
487 |
488 | - http://stackoverflow.com/questions/9259921/haskell-existential-quantification-in-detail
489 |
490 | - http://en.wikibooks.org/wiki/Haskell/Polymorphism
491 |
492 | ## Function types and why a -> b has b^a inhabitants
493 |
494 | ```
495 | 02:17 < bartleby> so I understand sum and product types, but why does a -> b have b^a cardinality?
496 | 02:23 < Iceland_jack> How many functions are there of type
497 | 02:23 < Iceland_jack> () -> b
498 | 02:23 < Iceland_jack> if b has 5 inhabitants?
499 | 02:23 < bartleby> 5
500 | 02:24 < Iceland_jack> which is 5^1 right?
501 | 02:24 < Iceland_jack> You'll want to look at Chris's blog: http://chris-taylor.github.io/blog/2013/02/10/the-algebra-of-algebraic-data-types/
502 | 02:24 < bartleby> yes
503 | 02:24 < bartleby> purple link, hm... I've been there, might've missed that.
504 | 02:25 < Iceland_jack> Now what about
505 | 02:25 < Iceland_jack> Bool -> b
506 | 02:25 < Iceland_jack> if b has 3 inhabitants
507 | 02:25 < Iceland_jack> You can gain your intuition by working these things out for increasingly more involved types
508 | 02:26 < bartleby> I was trying this, but it looked like a product type... I'm doing something wrong
509 | 02:26 < bartleby> let me see this case
510 | 02:26 < Iceland_jack> sure
511 | 02:27 < bartleby> wait, if I have one pattern for True and another for False, does it count as a single function? or two?
512 | 02:28 < Iceland_jack> If they're two patterns in the same function then it's the same function
513 | 02:28 < Iceland_jack> I.e. in the function definition
514 | 02:28 < Iceland_jack> f True = ...
515 | 02:28 < Iceland_jack> f False = ...
516 | 02:28 < Iceland_jack> 'f' is a single function
517 | 02:29 < Iceland_jack> and for the first ellipsis '...' you have one of three choices (b = {b1, b2, b3}) and same for the second one
518 | 02:29 < pyro-> does b^a include non total functions?
519 | 02:29 < Iceland_jack> no
520 | 02:29 < pyro-> why is that?
521 | 02:30 < Iceland_jack> Because it breaks all sorts of reasoning and makes it more complicated
522 | 02:30 < pyro-> :D
523 | 02:30 < bartleby> no? I thought that was what I was missing...
524 | 02:30 < Iceland_jack> bartleby: How many functions of type
525 | 02:30 < Iceland_jack> Bool -> ()
526 | 02:31 < bartleby> yes, that's where I'm confused. I'd guess one?
527 | 02:31 < Iceland_jack> Right, because the only choice is
528 | 02:31 < Iceland_jack> fn True = ()
529 | 02:31 < Iceland_jack> fn False = ()
530 | 02:31 < bartleby> matching True and False, but only returning ()
531 | 02:32 < Iceland_jack> so the number of function |Bool -> ()| is |()| ^ |Bool|
532 | 02:32 < Iceland_jack> |()| ^ |Bool|
533 | 02:32 < Iceland_jack> = 1 ^ 2
534 | 02:32 < Iceland_jack> = 1
535 | 02:32 < bartleby> ah, I think I get it
536 | 02:33 < Iceland_jack> And there are 2 functions from
537 | 02:33 < Iceland_jack> Bool -> ()
538 | 02:33 < Iceland_jack> conversely
539 | 02:33 < Iceland_jack> oops, () -> Bool I meant
540 | 02:33 < pyro-> Just by sitting in this channel I a learning things :D bartleby, how is it that cardinality of a type has interested you? I haven't even heard the term before
541 | 02:33 < Iceland_jack> 'const False' and 'const True' respectively
542 | 02:33 < bartleby> Iceland_jack: because 2^1
543 | 02:33 < Iceland_jack> Precisely
544 | 02:34 < Iceland_jack> pyro-: You should definitely read up on the 'Algebra of Algebraic Data Types'
545 | http://chris-taylor.github.io/blog/2013/02/10/the-algebra-of-algebraic-data-types/
546 | 02:34 < pyro-> thanks
547 | 02:34 < Iceland_jack> Lated parts discuss some more advanced uses
548 | 02:34 < Iceland_jack> *Later
549 | 02:34 < bartleby> pyro-: Algebraic Data Types, means you have an algebra for dealing with them.
550 | 02:35 < Iceland_jack> Just like you knew that
551 | 02:35 < Iceland_jack> 1 + 2 = 2 + 1
552 | 02:35 < Iceland_jack> in grade school so you can know that
553 | 02:35 < Iceland_jack> Either () Bool ≅ Either Bool ()
554 | 02:35 < bartleby> blowed my mind when I read about zippers, but I hadn't seen it with functions yet
555 | 02:36 < Iceland_jack> viewing (+) = Either, 1 = () and 2 = Bool
556 | 02:36 < Iceland_jack> It also means that you can define Bool as
557 | 02:36 < Iceland_jack> type Bool = Either () ()
558 | 02:36 < Iceland_jack> rather than
559 | 02:36 < Iceland_jack> data Bool = False | True
560 | 02:36 < Iceland_jack> since
561 | 02:36 < Iceland_jack> 1 + 1 ≅ 2
562 | 02:37 < Iceland_jack> Given the recent pattern synonyms extensions (PatternSynonyms) you can even use the same constructors and pattern match
563 | 02:37 < pyro-> Thats interesting
564 | 02:37 < Iceland_jack> type (+) = Either
565 | 02:37 < Iceland_jack> type BOOL = () + ()
566 | 02:37 < Iceland_jack> pattern TRUE = Right () :: BOOL
567 | 02:37 < Iceland_jack> pattern FALSE = Left () :: BOOL
568 | 02:38 < Iceland_jack> and then
569 | 02:38 < Iceland_jack> not :: BOOL -> BOOL
570 | 02:38 < Iceland_jack> not TRUE = FALSE
571 | 02:38 < Iceland_jack> not FALSE = TRUE
572 | 02:38 < pyro-> what abut values instead of types? 1 + 2 = 2 + 1 works for Int. what about algebra for values of other type?
573 | 02:38 < Iceland_jack> pyro-: You're not actually using numbers
574 | 02:38 < Iceland_jack> 1 is just a nice and confusing way to refer to the type ()
575 | 02:38 < pyro-> i understand
576 | 02:38 < bartleby> whoa, easy there boy! I'm overheating with 2^2 here
577 | 02:38 < Iceland_jack> not the value 1
578 | 02:38 < bartleby> :-D
579 | 02:38 < pyro-> thanks
580 | 02:39 < Iceland_jack> bartleby: Slowing down :)
581 | 02:39 < pyro-> actually that i'm not using numbers is kind of the point right?
582 | 02:39 < Iceland_jack> well it makes the analogy with elementary arithmetic clearer
583 | 02:39 < bartleby> pyro-: you are counting possible values of that type
584 | 02:40 < Iceland_jack> So you can write '2' for Bool because Bool has two things
585 | 02:40 < bartleby> so Either () Bool has three because: Left (), or Right True, or Right False
586 | 02:40 < Iceland_jack> Maybe Bool would be 3
587 | 02:40 < Iceland_jack> Yes exactly
588 | 02:40 < Iceland_jack> and thus
589 | 02:40 < Iceland_jack> Either () Bool ≅ Maybe Bool
590 | 02:41 < Iceland_jack> and also
591 | 02:41 < Iceland_jack> Maybe a ≅ Either () a
592 | 02:41 < Iceland_jack> If you define
593 | 02:41 < Iceland_jack> Maybe b = 1 + b
594 | 02:41 < Iceland_jack> Either a b = a + b
595 | 02:41 < Iceland_jack> then it becomes fairly clear
596 | 02:44 < bartleby> ah, I think it clicked here. I managed to list Bool -> Bool, four different functions
597 | 02:46 < Iceland_jack> and then for Bool -> Three where |Three| = 3 you have 3 independent choices for True and False so you have 3 * 3 = 3^2
598 | 02:46 < Iceland_jack> and so forth
599 | 02:46 < Iceland_jack> hope this clears things up a bit
600 | 02:46 < bartleby> I was unsure about partial fuctions, but now it makes sense. It's just a permutations of b I think (not sure if permutation is the right word)
601 | 02:47 < bartleby> how many arrangements with `a` elements of type `b` can I make?
602 | 02:51 < bartleby> Iceland_jack: thank you. I see that I have that page bookmarked, but I think I didn't get that Functions sections at the time
603 | 02:52 < bartleby> in fact, it's still confusing...
604 | 02:52 < bartleby> "Then each of First, Second and Third can map to two possible values, and in total there are 2⋅2⋅2 = 2^3 = 8 functions of type Trio -> Bool"
605 | 02:53 < bartleby> counting like this I was only seeing First->True, First->False, Second->True, Second->False... 6, like a product
606 | 02:54 < Iceland_jack> You have to map all the values
607 | 02:54 < Iceland_jack> so the first function might be
608 | 02:54 < Iceland_jack> f1 First = False
609 | 02:54 < Iceland_jack> f1 Second = False
610 | 02:54 < Iceland_jack> f1 Third = False
611 | 02:54 < Iceland_jack> And the second function might be
612 | 02:54 < Iceland_jack> f2 First = True
613 | 02:54 < Iceland_jack> f2 Second = False
614 | 02:54 < Iceland_jack> f2 Third = False
615 | 02:54 < bartleby> yeah, I missed that. Thinking about combinations is easier IMO. True True True, True True False, ...
616 | 02:55 < bartleby> reminds me of truth tables :)
617 | 02:55 < Iceland_jack> writing False as 0 and True as 1 you get
618 | 02:55 < Iceland_jack> Trio -> Bool = { 000, 001, 010, 011, 100, 101, 110, 111 }
619 | 02:55 < Iceland_jack> with
620 | 02:55 < Iceland_jack> |Trio -> Bool|
621 | 02:56 < Iceland_jack> = |Bool| ^ |Trio|
622 | 02:56 < dibblego> a function of the type X -> Y has Y^X possibilites
623 | 02:56 < Iceland_jack> = 2 ^ 3 = 8
624 | 02:56 < Iceland_jack> right :)
625 | 02:57 < Iceland_jack> so a function from
626 | 02:57 < Iceland_jack> Trio -> Bool
627 | 02:57 < Iceland_jack> has the following implementations
628 | 02:57 < Iceland_jack> > replicateM 3 [0, 1]
629 | 02:57 < lambdabot> [[0,0,0],[0,0,1],[0,1,0],[0,1,1],[1,0,0],[1,0,1],[1,1,0],[1,1,1]]
630 | 02:58 < Iceland_jack> and
631 | 02:58 < Iceland_jack> Quad -> Bool
632 | 02:58 < Iceland_jack> > replicateM 4 [0, 1] -- etc.
633 | 02:58 < lambdabot> [[0,0,0,0],[0,0,0,1],[0,0,1,0],[0,0,1,1],[0,1,0,0],[0,1,0,1],[0,1,1,0],[0,1,...
634 | 02:58 < Iceland_jack> > [ length (replicateM domainSize [0,1]) | domainSize <- [0..6] ]
635 | 02:58 < lambdabot> [1,2,4,8,16,32,64]
636 | 02:59 < Iceland_jack> > [ 2^domainSize | domainSize <- [0..6] ]
637 | 02:59 < lambdabot> [1,2,4,8,16,32,64]
638 | 03:01 < bartleby> > replicateM 2 [0,1,2]
639 | 03:01 < lambdabot> [[0,0],[0,1],[0,2],[1,0],[1,1],[1,2],[2,0],[2,1],[2,2]]
640 | 03:01 < bartleby> so that's Bool -> Trio. nice
641 | 03:01 < Iceland_jack> Which has 3^2 = 9 elements not to put too fine a point on it
642 | 03:02 * bartleby is counting subarrays
643 | 03:02 < bartleby> yup, nine
644 | 03:02 < bartleby> now it makes sense, thanks
645 | 03:04 < spion> so basically, you want the number of the possible tables, rather than the number of items in a table?
646 | 03:04 < spion> :)
647 | 03:04 < dibblego> this is why you find there are 4 implementations of (Bool -> Bool)
648 | 03:05 < Iceland_jack> yes since you can interpret each table as a function definition
649 | 03:05 < Iceland_jack> True | False
650 | 03:05 < Iceland_jack> -----+------
651 | 03:05 < Iceland_jack> a | b
652 | 03:05 < spion> right
653 | 03:05 < Iceland_jack> and
654 | 03:05 < Iceland_jack> replicateM (length xs) xs
655 | 03:05 < Iceland_jack> should always have n^n elements given n = length xs
656 | 03:06 < Iceland_jack> can also be rewritten as
657 | 03:06 < Iceland_jack> (length >>= replicateM) xs
658 | 03:07 < Iceland_jack> > map (length . (length>>=replicateM) . flip replicate ()) [0..7]
659 | 03:07 < lambdabot> [1,1,4,27,256,3125,46656,823543]
660 | 03:07 < Iceland_jack> > [ n^n | n <- [0..7] ]
661 | 03:07 < lambdabot> [1,1,4,27,256,3125,46656,823543]
662 | ```
663 |
664 | ## Applicative and liftA2
665 |
666 | ```
667 | 02:42 < dibblego> > liftA2 (+) [1,2,3] [30,40,50]
668 | 02:42 < lambdabot> [31,41,51,32,42,52,33,43,53]
669 | 02:42 < blueclaude> Thanks dibblego
670 | 02:42 < dibblego> ! [1+30,1+40,1+50,2+30,2+40,2+50,3+30,3+40,3+50]
671 | 02:43 < benzrf> blueclaude: (<*>) on the list applicative is cartesian
672 | product, but applying the first item to the second
673 | 02:43 < benzrf> > [(++"foo"), (++"bar")] <*> ["test", "othertest", "more"]
674 | 02:43 < lambdabot>
675 | ["testfoo","othertestfoo","morefoo","testbar","othertestbar","morebar"]
676 | 02:44 < dibblego> > join (Just (Just 4))
677 | 02:44 < lambdabot> Just 4
678 | 02:44 < dibblego> > join (Just Nothing)
679 | 02:44 < lambdabot> Nothing
680 | 02:44 < benzrf> > join []
681 | 02:45 < lambdabot> []
682 | 02:45 < damncabbage> > [(+ 1), (+ 2)] <*> [1,2,3]
683 | 02:45 < lambdabot> [2,3,4,3,4,5]
684 | 02:45 < dibblego> Maybe is cosemimonad, but not a comonad
685 | 02:47 < dibblego> bitemyapp: [] is also cosemimonad but not comonad
686 | ```
687 |
688 | ```haskell
689 | liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
690 | (<*>) :: Applicative f => f (a -> b) -> f a -> f b
691 | -- by itself, cosemimonad
692 | class Functor w =>
693 | extend :: (w a -> b) -> w a -> w b
694 | ```
695 |
696 | ## RankNTypes with CPS'y example
697 |
698 | ```haskell
699 | myFunc :: (forall a. a -> (a -> a) -> a) -> Int
700 | myFunc f = f 0 (+1)
701 |
702 | -- won't work
703 | -- otherFunc :: (a -> (a -> a) -> a) -> Int
704 | -- otherFunc f = f 0 (+1)
705 |
706 | -- use:
707 | -- myFunc (flip ($))
708 | ```
709 |
710 | ```
711 | 22:42 < mm_freak_> because 'f' is polymorphic, myFunc gets to apply it
712 | to its own choice of types
713 | 22:42 < mm_freak_> in particular it can make different choices in
714 | different places
715 | 22:43 < mm_freak_> the "forall" really just means that the function
716 | implicitly takes a type argument
717 | 22:44 < bitemyapp> mm_freak_: I think part of the problem is the difference between
718 | 22:44 < bitemyapp> (forall a. a -> (a -> a) -> a) -> Int
719 | 22:44 < bitemyapp> vs.
720 | 22:44 < bitemyapp> forall a. (a -> (a -> a) -> a) -> Int
721 | 22:44 < bitemyapp> yes?
722 | 22:44 < bitemyapp> the latter being implicitly the case in Haskell.
723 | 22:44 < mm_freak_> yes, but think about it… think really really simple in this case
724 | 22:45 < mm_freak_> in the former case myFunc receives a polymorphic function, so myFunc
725 | gets to choose the type
726 | 22:45 < mm_freak_> in the latter case myFunc itself is polymorphic, so the applier of
727 | myFunc gets to choose it
728 | 22:45 < mm_freak_> notice that in the former case myFunc is
729 | monomorphic!
730 | 22:46 < mm_freak_> yeah… its type isn't quantified over any type
731 | variables
732 | 22:46 < bitemyapp> mm_freak_: but the lambda passed to it is?
733 | 22:46 < mm_freak_> yeah
734 | 22:46 < bitemyapp> okay, yes.
735 | 22:46 < bitemyapp> so we're assigning/shifting around polymorphism
736 | 22:46 < bitemyapp> between the top level function the func arg
737 | 22:46 < bitemyapp> based on the ranks/nesting
738 | 22:46 < bitemyapp> / scope'ish
739 | ```
740 |
741 | ## Functor, algebras, Coyoneda
742 |
743 | * * * * *
744 |
745 | bitemyapp edited 4 days ago | link | delete | reply
746 |
747 | I realize this is partly because the examples are in Scala, but none
748 | of this gets at what a Functor really is.
749 |
750 | Functor is an algebra.
751 |
752 | Functor is an algebra with one operation, usually called map.
753 |
754 | That one operation has a type something like:
755 |
756 | ```haskell
757 | (a -> b) -> f a -> f b
758 | ```
759 |
760 | That one operation should respect identity:
761 |
762 | ```
763 | map id = id
764 | ```
765 |
766 | And that one operation should be associative:
767 |
768 | ```
769 | map (p . q) = (map p) . (map q)
770 | ```
771 |
772 | That’s it people. That’s it. Functor is a very weak structure. Many
773 | things can be functor. Many of those things will not look anything
774 | like a “list”, “collection”, or even a “data structure”.
775 |
776 | Understanding free objects, free versions of these algebraic
777 | structures, can lend a more faithful intuition for what these things
778 | are.
779 |
780 | Glancing at Coyoneda (the free functor) should give one some idea of
781 | why you’re not dealing with something that has anything to do with
782 | lists.
783 |
784 | Want to know more?
785 |
786 | You know the drill: https://github.com/bitemyapp/learnhaskell
787 |
788 | Edit:
789 |
790 | Since I take great satisfaction in excising misunderstandings, I’m
791 | going to include a Functor instance that should help drop the
792 | “collections” oriented view of what they are.
793 |
794 | ```haskell
795 | -- (->) or -> is the type constructor for functions
796 | -- a -> a, the identity function's type is a type of
797 | -- -> taking two parameters of the same type (a and a)
798 | -- (->) a a analogous to Either a b
799 | instance Functor ((->) r) where
800 | map = (.)
801 |
802 | -- (.) or . is function composition
803 | -- (.) :: (b -> c) -> (a -> b) -> a -> c
804 | -- more on this Functor instance:
805 | -- http://stackoverflow.com/questions/10294272/confused-about-function-as-instance-of-functor-in-haskell
806 | ```
807 |
808 | Bonus round for upvoting me:
809 |
810 | http://www.haskellforall.com/2012/09/the-functor-design-pattern.html
811 | http://hackage.haskell.org/package/kan-extensions-3.7/docs/Data-Functor-Coyoneda.html
812 | http://oleksandrmanzyuk.wordpress.com/2013/01/18/co-yoneda-lemma/
813 | http://www.reddit.com/r/haskell/comments/17a33g/free_functors_the_reason_free_and_operational_are/c83p8k2
814 | https://gist.github.com/thoughtpolice/5843762
815 |
816 | * * * * *
817 |
818 | tel 4 days ago | link | reply
819 |
820 | ```
821 | Understanding free objects, free versions of these algebraic
822 | structures, can lend a more faithful intuition for what these things
823 | are.
824 | ```
825 |
826 | This is a super great point—it also, meaningfully, applies to other
827 | structures like Monads, Applicatives, or Monoids, Categories,
828 | Arrows. Really quickly, here’s Yoneda and Coyoneda (the “two” free
829 | functors)
830 |
831 | ```haskell
832 | newtype Yoneda f a = Yoneda { runYoneda :: forall b . (a -> b) -> f b }
833 | data Coyoneda f b where Coyoneda :: f a -> (a -> b) -> Coyoneda f b
834 | ```
835 |
836 | In each case we see that functor tends to mean having a parametric
837 | structure (the f) and a method of transforming the parameter to
838 | something else (the functions a -> b). When we “collapse” this free
839 | view of a functor we get to decide if, how, when, and why we combine
840 | that structure and its mapping function. For lists we, well, map
841 | it. For something like
842 |
843 | ```haskell
844 | data Liar a = Liar -- note that `a` does not appear on the right side
845 | ```
846 |
847 | we just throw the mapping function away.
848 |
849 | (Another key point that’s a bit harder to see is that if you map the
850 | Yoneda/Coyoneda formulation repeatedly it does not store each and
851 | every mapping function but instead composes them all together and
852 | retains only that composition. This ensures that functors cannot “see”
853 | how many times fmap has been called. That would let you violate the
854 | functor laws!)
855 |
856 | * * * * *
857 |
858 | gclaramunt 3 days ago | link | reply
859 |
860 | Do you have any reference of functor being an algebra? I’m intrigued
861 |
862 | Since we’re clarifying what a functor is, I guess is worth noting that
863 | you’re talking about endofunctors in the (idealized) Hask category. In
864 | category theory, a functor is defined by two mappings: one for objects
865 | in the category and one for arrows, that must preserve identity and
866 | composition (the laws you mention). Since the mapping of objects is
867 | already given by the type constructor, here one needs to provide only
868 | the mapping of functions but it kind of irks me when ppl. say a
869 | functor is only defined by “map” :)
870 |
871 | * * * * *
872 |
873 | tel 2 days ago | link | reply
874 |
875 | Functor is definitely an algebra. Its rules mean that it has tight
876 | relation to certain functors in CT.
877 |
878 | * * * * *
879 |
880 | gclaramunt edited 2 days ago | link | reply
881 |
882 | Interesting… any refereces I can read? Or you’re talking about
883 | F-algebras?
884 |
885 | * * * * *
886 |
887 | tel 2 days ago | link | reply
888 |
889 | I mean “algebra” as “set of operations and equalities”.
890 |
891 | * * * * *
892 |
893 | gclaramunt 2 days ago | link | reply
894 |
895 | Ok. To be honest, I need to familiarize myself with the definition of
896 | algebra, is just that I had never heard this before :)
897 |
898 | * * * * *
899 |
900 | tel 1 day ago | link | reply
901 |
902 | It’s an incredibly overloaded term, tbh. In the context of abstract
903 | algebra you’d probably want to think of a (G, L)-algebra as a set
904 | inductively defined by generators G and laws L. For instance, here’s a
905 | “free” monoid algebra (note that this isn’t a free monoid, but a “free
906 | monoid algebra” or a “free algebra of the monoid type” or a “(monoid,
907 | {})-algebra” maybe)
908 |
909 | ```haskell
910 | data FMonoid where
911 | Fmempty :: FMonoid
912 | Fmappend :: FMonoid -> FMonoid -> FMonoid
913 |
914 | class Monoid FMonoid where -- this is wrong! doesn't follow laws!
915 | mempty = Fmempty
916 | mappend = Fmappend
917 | ```
918 |
919 | note that it has all the “generators” of the typeclass Monoid but
920 | follows none of the rules (mempty <> mempty != mempty). Typically we
921 | also want to add a set of constants to form the smallest free algebra
922 | over a set
923 |
924 | ```haskell
925 | data FMonoid a where
926 | Embed :: a -> FMonoid a
927 | Fmempty :: FMonoid a
928 | Fmappend :: FMonoid a -> FMonoid a -> FMonoid a
929 | ```
930 |
931 | * * * * *
932 |
933 | gclaramunt 1 day ago | link | reply
934 |
935 | Really interesting, thanks a lot! Now I’m trying to see how this ties
936 | to the Functor typeclass: G are the instance constructors and the
937 | functor laws make L ? I think I’m missing an important piece of the
938 | puzzle here :)
939 |
940 | * * * * *
941 |
942 | tel 1 day ago | link | reply
943 |
944 | You’re not, that’s basically it.
945 |
946 | ```haskell
947 | data FFunctor f a where
948 | EmbedFunctor :: f a -> FFunctor f a
949 | Ffmap :: (a -> b) -> FFunctor f a -> FFunctor f b
950 | ```
951 |
952 | This lets you build the free (Functor, {})-algebra over some initial
953 | type f. If we translate it naively then it doesn’t follow the laws
954 |
955 | ```haskell
956 | class Functor (FFunctor f) where -- wrong!
957 | fmap = Ffmap
958 | ```
959 |
960 | but we can implement it properly if we’re a little more clever
961 |
962 | ```haskell
963 | class Functor (FFunctor f) where
964 | fmap f x = case x of
965 | EmbedFunctor fa -> Ffmap f x
966 | Ffmap g fa -> Ffmap (f . g) fa
967 | ```
968 |
969 | We need one more function, though, since we can’t use EmbedFunctor
970 | directly without exposing information about whether or not we’ve ever
971 | fmaped this functor (which shouldn’t be possible to access, that’s
972 | what fmap id = id says)
973 |
974 | ```haskell
975 | embed :: f a -> FFunctor f a
976 | embed fa = Ffmap id (EmbedFunctor fa)
977 | ```
978 |
979 | And now, if we think about it, we can see that every value of FFunctor
980 | constructed using embed and fmap is of the form
981 |
982 | ```haskell
983 | Ffmap fun (EmbedFunctor fa)
984 | ```
985 |
986 | And so that EmbedFunctor constructor is totally superfluous. Let’s
987 | remove it
988 |
989 | ```haskell
990 | data FFunctor f a where
991 | Ffmap :: (a -> b) -> f a -> FFunctor f b
992 |
993 | embed :: f a -> FFunctor f a
994 | embed fa = Ffmap id fa
995 | ```
996 |
997 | And—well—this is just CoYoneda again!
998 |
999 | ```haskell
1000 | lower :: Functor f => FFunctor f a -> f a
1001 | lower (Ffmap f fa) = fmap f fa
1002 | ```
1003 |
1004 | * * * * *
1005 |
1006 | gclaramunt about 9 hours ago | link | reply
1007 |
1008 | Nice Haven’t digested it properly but I see the trick is to capture
1009 | the functor with a datatype (is the same thing with free monads,
1010 | right?) Now is easier to see from where CoYoneda comes, thanks! (you
1011 | did show me an important piece of the puzzle :P )
1012 |
1013 | ## Magma, parallelism, free monoid
1014 |
1015 | - [Original post](https://www.fpcomplete.com/user/bss/magma-tree)
1016 |
1017 | - [Guy Steele talk referenced](https://vimeo.com/6624203)
1018 |
1019 | - [Comment thread](http://www.reddit.com/r/haskell/comments/2corq6/algebraic_terraforming_trees_from_magma/)
1020 |
1021 | * * * * *
1022 |
1023 | edwardkmett 7 points an hour ago
1024 |
1025 | Much of Guy Steele's work here pertained to a desire to be able to parallelize calculation. This is a laudable goal.
1026 | The main issue with a naïve magma approach Steele proposed for Fortress is that you have zero guarantees about efficient splittability. All the mass of your magma could be on one side or the other.
1027 |
1028 | The benefit is that without those guarantees infinite magmas make sense in a lazy language. You can have infinitely large trees just fine, that go off to infinity at any point not just at the right.
1029 |
1030 | This has a certain pleasing structure to it. Why? Well, lists aren't really the free monoid if you allow for infinitely recursive use of your monoid! You have unit and associativity laws and by induction you can apply them a finite number of times, but reassociating an infinite tree from the left to the right requires an infinite number of steps, taking us out of the constructive world we can program. So ultimately a free Monoid (allowing for infinite monoids) is something like Sjoerd Visscher's
1031 |
1032 | ```haskell
1033 | newtype Free p = Free { runFree :: forall r. p r => (a -> r) -> r }
1034 |
1035 | type List = Free Monoid
1036 | ```
1037 |
1038 | Here we borrow the assumption of unit and association from the target r and generate something using it. It is an almost vacuous but now correct construction, whereas the association to the right to make a list required us to be able to right associate infinite trees. You can view this as a sort of quotient on a magma, where you guarantee to only consume it with monoidal reductions.
1039 |
1040 | Binding/substituting on a (unital) magma can now take longer than O(n), why? Because now I have to walk past all the structure. You can replace this with Oleg and Atze's "Reflection without Remorse", but walking down a unital Magma structure doesn't decrease n necesssarily.
1041 |
1042 | In the absence of infinite trees, you usually want some form of balance depending on what you want to do with the structure. e.g. turning it into a catenable deque gives you efficient access to both ends and lets you still glue in O(1) or O(log n).
1043 |
1044 | Switching to a finger tree gives you guaranteed O(log n) splits, but now merges go from O(1) to O(log n)
1045 | In a general magma the split is potentially completely lopsided. You can 'steal work' but as often as not you likely steal a single unit, or in a unital magma, possibly nothing.
1046 |
1047 | The cost of these richer structures is you lose the continuous extension to the infinite case, but when trading O(n) or worse for O(log n) it is often worth making that trade-off.
1048 |
1049 | ## Why non-strictness (laziness) needs to be the default
1050 |
1051 | ```
1052 | 23:20 < slack1256> It is folklore that lazy evaluation compose better, usually showing how it supports separated
1053 | generators/consumers as in "Why functional programming matters", but does this notion of
1054 | composition goes further?
1055 | 23:20 < Cale> slack1256: In what sense?
1056 | 23:21 < slack1256> as in, if some function is not based on generator/consumer based can still benefit from
1057 | laziness?.
1058 | 23:21 < slack1256> (this is a problem of me having lack of imagination)
1059 | 23:22 < Cale> slack1256: Most functions are consuming or generating something. Those which produce or consume
1060 | larger structures with many parts that could be evaluated separately tend to benefit from laziness.
1061 | 23:22 < tabemann> from what I gather, though, lazy lists aren't as useful as that paper purported
1062 | 23:23 < Cale> We use them all the time
1063 | 23:23 < haasn> Laziness can play well into parallelism
1064 | 23:23 < haasn> Or, rather, nonstrictness
1065 | 23:23 < haasn> Only evaluate the strict parts immediately, evaluate parts that don't have to be forced yet in parallel
1066 | 23:23 < tabemann> what I mean is that there are better lazy sequence data structures than the list
1067 | 23:23 < Cale> Oh?
1068 | 23:23 < Cale> Lists are absolutely perfect for what they are
1069 | 23:24 < slack1256> Mmm you're right, most functions are consuming or generating something, specially in a pure
1070 | language.
1071 | 23:24 < Cale> If you plan on iterating through a list of things in order, then lists present you with about as
1072 | concise a representation as possible.
1073 | 23:24 < Cale> slack1256: Lists are essentially our loops
1074 | 23:25 < Cale> and it helps sometimes that they can be infinite, or combinatorially large
1075 | 23:25 < Cale> for the same reason that you might want to have an infinite loop, or one which potentially iterates
1076 | more times than you'll practically ever want to actually have the loop body occur.
1077 | 23:26 < slack1256> In "more points for lazy evaluation" augustss shows that laziness enable efficient higher-order
1078 | functions, and bob concedes that point that in strict languages that really hurts because you
1079 | have to use manual recursion.
1080 | 23:26 < Cale> yep
1081 | 23:26 < slack1256> Maybe I should really learn SML to appreciate more the benefits of laziness
1082 | 23:27 < josephle> then you'll lament the lack of backpack in Haskell ;)
1083 | 23:28 < Cale> It really needs to be the default for that reason: if the default is to be strict, when you find the
1084 | functions that you want to compose in your library, the chances are good that whoever wrote it won't
1085 | have thought about your use case, and you'll need to rewrite it to be explicitly lazy, which defeats
1086 | a lot of the point of having things be compositional.
1087 | 23:31 < Cale> Whereas strictness is just slightly more rarely required, and tends to be the kind of thing that you
1088 | can't ignore when you really need it, because your program's performance will suffer dramatically. So
1089 | it just becomes a matter of learning to spot the places where it'll be important. The rule of thumb I
1090 | use is this: if you're collapsing many individual bits of data down into a single thing which depends
1091 | on all of them
1092 | 23:31 < Cale> and can't be partially evaluated, that's where you want some sort of strict higher-order function
1093 | like foldl' or some explicit strictness annotations.
1094 | 23:32 < Cale> Basically, things you'd think of as accumulations of some sort. You want to avoid building up large
1095 | expressions in memory composed of strict functions (functions that must pattern match on their input
1096 | to produce any part of their result).
1097 | 23:34 < Cale> So for instance, when you're repeatedly updating the contents of an IORef, or recursively updating an
1098 | accumulating parameter without matching on it, you want to be careful there.
1099 | ```
1100 |
1101 | ## Functor for Reader
1102 |
1103 | ```
1104 | < OscarZ> instance Functor ((->) t) where
1105 | < ajcoppa> OscarZ: you know about (->) from the types of functions, right?
1106 | < OscarZ> ajcoppa: yes
1107 | < ajcoppa> ((->) t) is a partial definition that describes a function that takes in a value of type t
1108 | < OscarZ> ajcoppa: im a bit confused.. so does "instance Functor ((->) t) where" mean that were defining that any function that takes a parameter is a Functor instance?
1109 | < ajcoppa> OscarZ: yep!
1110 | < OscarZ> ajcoppa: is that different than saying instance Functor (a -> b) where ?
1111 | < ajcoppa> OscarZ: it is different, because functors need to have a kind of * -> *
1112 | < ajcoppa> as an example, when defining a functor for Optional, you don't say instance Functor (Optional Int) where -- you say instance Functor Optional where
1113 | < pjdelport> OscarZ: "Functor ((->) r)" is a lot like "Functor (Either a)" and "Functor ((,) a)"
1114 | < pjdelport> OscarZ: You're declaring an instance for a partially-applied type, which itself will get applied to a remaining type (which is the "slot" that the functor operates on)
1115 | < pjdelport> OscarZ: So in the same sense that "instance Functor Maybe where ..." means: fmap :: (a -> b) -> Maybe a -> Maybe b
1116 | < pjdelport> "instance Functor ((,) x) where ..." means: fmap (a -> b) -> (x,a) -> (x,b)
1117 | < pjdelport> Or in equivalent syntax: fmap :: (a -> b) -> ((,) x) a -> ((,) x) b
1118 | < pjdelport> where ((,) x) is what "f" gets replaced with
1119 | < pjdelport> So for "instance Functor (Either x) where ...", you have fmap :: (a -> b) -> Either x a -> Either x b
1120 | < pjdelport> and for "instance Functor ((->) r) where ...", you have fmap :: (a -> b) -> (->) r a -> (->) r b
1121 | < pjdelport> or equivalently: fmap :: (a -> b) -> (r -> a) -> (r -> b)
1122 | < OscarZ> oh.. i think i finally get it
1123 | < pjdelport> OscarZ: To read that more intuitively, just remember what the "slot" that fmap operates on is for each Functor instance: for lists, the "slots" are all of a list's elements. For Maybe, the "slots" are Just values (if any). For Either, the "slots" are Right values.
1124 | < pjdelport> OscarZ: For functions, (->), the "slot" that you're operating on is the *result* of the function.
1125 | < pjdelport> So applying "fmap f" to a function value like (r -> a) uses f to "replace" the result value 'a' with a 'b', yielding a new function (r -> b).
1126 | < pjdelport> (Just like how applying "fmap f" to a list value [a] uses f to "replace" each element 'a' with a 'b', yielding a new list [b].)
1127 | < OscarZ> to implement Functor functions with signature a -> a -> a, should it be instance Function ((->) r t) where ?
1128 | < pjdelport> OscarZ: a -> a -> a, or (a -> (a -> a)), is just a special case of (a -> b) where b = (a -> a)
1129 | < OscarZ> pjdelport: so like Functor (Either a) matches (Either a) part in Either a b, Functor ((->) r) matches just the first a in a -> (a -> a) ? in this case the slot would be of type (a -> a) ?
1130 | < pjdelport> OscarZ: Yep.
1131 | < OscarZ> ok thanks.. i think i get.. apparently im not used to functions as first-class citizens :) somehow got confused when it was a function instead of something like Either a b
1132 | < ajcoppa> it is very normal to need some time to wrap your head around this instance in particular
1133 | < pjdelport> Yeah, it can be mind-bending. :)
1134 | < pjdelport> but it's good exercise
1135 | < OscarZ> you guys are good at explaining :)
1136 | < OscarZ> you could teach FP to a rotten cabbage
1137 | ```
1138 |
1139 | ```haskell
1140 | -- n.b. * means we're showing kind signatures, not type signatures.
1141 | -- application of type arguments
1142 |
1143 | data (->) a b
1144 | (->) :: * -> * -> *
1145 | ((->) e) :: * -> *
1146 |
1147 | instance Functor ((->) r) where
1148 | fmap = (.)
1149 |
1150 | instance Functor ((->) r) where
1151 | fmap f g = (\x -> f (g x))
1152 |
1153 |
1154 | data (,) a b = (,) a b
1155 | (,) :: * -> * -> *
1156 | ((,) a) :: * -> *
1157 |
1158 | instance Functor ((,) a) where
1159 | fmap f (x,y) = (x, f y)
1160 |
1161 |
1162 | newtype Reader r a = Reader { runReader :: r -> a }
1163 | Reader :: * -> * -> *
1164 | (Reader r) :: * -> *
1165 |
1166 | instance Functor (Reader r) where
1167 | fmap f m = Reader $ \r -> f (runReader m r)
1168 |
1169 | instance Monad (Reader r) where
1170 | return a = R $ \_ -> a
1171 | m >>= k = R $ \r -> runReader (k (runReader m r)) r
1172 |
1173 |
1174 | class Functor (f :: * -> *) where
1175 | fmap :: Functor f => (a -> b) -> f a -> f b
1176 |
1177 | class Monad (m :: * -> *) where
1178 | (>>=) :: m a -> (a -> m b) -> m b
1179 | return :: a -> m a
1180 | ```
1181 |
1182 | ## Join for Reader
1183 |
1184 | ```
1185 | 18:51 < OscarZ> about join... im wondering about join (+) 7 = 14
1186 | 19:14 < pjdelport> OscarZ: join for Reader becomes even more Intuitive when you pronounce Reader e a as "function
1187 | from an environment e to a"
1188 | 19:15 < pjdelport> Then "Reader e (Reader e a)" is just "function from an environment e to function from an
1189 | environment e to a"
1190 | 19:16 < pjdelport> And joining means turning that into just one "function from an environment e to a"
1191 | 19:16 < pjdelport> And saying it like that should (hopefully) make the implementation and what it does more obvious
1192 | :)
1193 | 19:23 < pjdelport> I still think just pondering "type Reader e a = e -> a" is a great way to to get that "a ha"
1194 | moment
1195 | ```
1196 |
1197 | ```haskell
1198 | λ> join (+) 7
1199 | 14
1200 | λ> join (*) 7
1201 | 49
1202 | ```
1203 |
1204 | ## Reversing a list operation that uses >>=
1205 |
1206 | SpacemanInBikini:
1207 |
1208 | I made a function in Haskell that takes in a number and returns a list of integers from 1 to that number.
1209 |
1210 | ```haskell
1211 | gen x = [1..x]
1212 | gen 4
1213 | -> [1, 2, 3, 4]
1214 | ```
1215 |
1216 | As I was learning about the monads I noticed that I could bind a list into this function as it has the type of (a -> m a).
1217 |
1218 | ```haskell
1219 | [2, 3, 6, 1, 4] >>= gen
1220 | -> [1, 2, 1, 2, 3, 1, 2, 3, 4, 5, 6, 1, 1, 2, 3, 4]
1221 | ```
1222 |
1223 | Then I wanted to write a function that would reverse the (>>= gen) and i came up with this function.
1224 |
1225 | ```haskell
1226 | revgen (x:xs) =
1227 | case (null xs) of
1228 | False -> case ((head xs) - x) of
1229 | 1 -> 1 + revgen xs
1230 | otherwise -> 0 : revgen xs
1231 | True -> 1 : []
1232 | ```
1233 |
1234 | But it doesn't work, I think because of the revgen returning both numbers and lists. But I can't come up with a way to do this any other way with one function. Also the nested cases look ugly as hell but I quess they work as intented because at least it compiles.
1235 |
1236 | * * * * *
1237 |
1238 | meditans:
1239 |
1240 | Given the definitions:
1241 |
1242 | ```haskell
1243 | gen x = [1..x]
1244 | f xs = xs >>= gen
1245 | ```
1246 |
1247 | If you want a function g such that g . f = id, I think you could use:
1248 |
1249 | ```haskell
1250 | g xs = map last $ groupBy (<) xs
1251 | ```
1252 |
1253 | ```haskell
1254 | λ> groupBy (<) [1, 2, 1, 2, 3, 1, 2, 3, 4, 5, 6, 1, 1, 2, 3, 4]
1255 | [[1,2],[1,2,3],[1,2,3,4,5,6],[1],[1,2,3,4]]
1256 |
1257 | λ> map last $ groupBy (<) [1, 2, 1, 2, 3, 1, 2, 3, 4, 5, 6, 1, 1, 2, 3, 4]
1258 | [2,3,6,1,4]
1259 | ```
1260 |
1261 | ## Programmatically finding the inverse of a bijective function
1262 |
1263 | tel:
1264 |
1265 | If the range is finite and the domain has decidable equality
1266 |
1267 | ```haskell
1268 | class Finite a where
1269 | -- x :: a iff elem x universe = True
1270 | universe :: [a]
1271 |
1272 | inv :: (Eq b, Finite a) => (a -> b) -> (b -> a)
1273 | inv f b = let [a] = [ a | a <- universe, f a == b ] in a
1274 | ```
1275 |
1276 | The pattern matching above will fail if f is not bijective.
1277 |
--------------------------------------------------------------------------------