├── 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 | --------------------------------------------------------------------------------