[READ-ONLY] Mirror of https://github.com/Schniz/functional-programming-smalltalks. small talks about fp in js for Keywee
0

Configure Feed

Select the types of activity you want to include in your feed.

first iteration

Gal Schlezinger (Jan 4, 2018, 11:08 AM +0200) 4f988fc8

+263
+1
.gitignore
··· 1 + node_modules
+6
package.json
··· 1 + { 2 + "dependencies": { 3 + "ramda": "^0.25.0", 4 + "sanctuary": "^0.14.1" 5 + } 6 + }
+36
yarn.lock
··· 1 + # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 + # yarn lockfile v1 3 + 4 + 5 + ramda@^0.25.0: 6 + version "0.25.0" 7 + resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.25.0.tgz#8fdf68231cffa90bc2f9460390a0cb74a29b29a9" 8 + 9 + sanctuary-def@0.14.0: 10 + version "0.14.0" 11 + resolved "https://registry.yarnpkg.com/sanctuary-def/-/sanctuary-def-0.14.0.tgz#cebbec591f5a9b211b34b4b942999d6534010fa8" 12 + dependencies: 13 + sanctuary-type-classes "7.1.x" 14 + sanctuary-type-identifiers "2.0.x" 15 + 16 + sanctuary-type-classes@7.1.1, sanctuary-type-classes@7.1.x: 17 + version "7.1.1" 18 + resolved "https://registry.yarnpkg.com/sanctuary-type-classes/-/sanctuary-type-classes-7.1.1.tgz#084bbd3168d72e60a059f6f78f5b7e391e68ffbb" 19 + dependencies: 20 + sanctuary-type-identifiers "1.0.x" 21 + 22 + sanctuary-type-identifiers@1.0.x: 23 + version "1.0.0" 24 + resolved "https://registry.yarnpkg.com/sanctuary-type-identifiers/-/sanctuary-type-identifiers-1.0.0.tgz#e8f359f006cb5e624cfb8464603fc114608bde9f" 25 + 26 + sanctuary-type-identifiers@2.0.1, sanctuary-type-identifiers@2.0.x: 27 + version "2.0.1" 28 + resolved "https://registry.yarnpkg.com/sanctuary-type-identifiers/-/sanctuary-type-identifiers-2.0.1.tgz#fc524cf6dd92cebfcbb0dd9509eff193159a20ed" 29 + 30 + sanctuary@^0.14.1: 31 + version "0.14.1" 32 + resolved "https://registry.yarnpkg.com/sanctuary/-/sanctuary-0.14.1.tgz#3bf7613ec79ad28e5997b2901992e5992d3a047b" 33 + dependencies: 34 + sanctuary-def "0.14.0" 35 + sanctuary-type-classes "7.1.1" 36 + sanctuary-type-identifiers "2.0.1"
+84
talk01/01_first-class-functions.md
··· 1 + Functions in JavaScript are declared this way: 2 + 3 + ```js 4 + function fn(arg1, arg2, ...spread) { 5 + return value; 6 + } 7 + ``` 8 + 9 + Functions can be stored in a variable/constant so they can also be declared this way: 10 + 11 + ```js 12 + const fn = function(arg1, arg2, ...spread) { 13 + return value; 14 + }; 15 + ``` 16 + 17 + Or you can use arrow functions. This is nice and helps with JS context issues: 18 + 19 + ```js 20 + const fn = (arg1, arg2, ...spread) => value; 21 + ``` 22 + 23 + Functions also have methods on them, because they're javascript objects! 24 + 25 + ```js 26 + const fn = function(arg) { 27 + return [this, arg]; 28 + }; 29 + const fnBounded = fn.bind("a", "hey"); 30 + console.log(fnBounded()); 31 + ``` 32 + 33 + We can even extend the functionalities by extending `Function.prototype` but we won't do it right now 34 + 35 + Because all the above, functions can be arguments as well (callbacks) 36 + 37 + ```js 38 + const apply = (fn, value) => fn(value); 39 + const result = apply(x => x + 1, 2); 40 + console.log({ result }); 41 + 42 + // You can pass functions without initiating them on the spot 43 + const increment = x => x + 1; 44 + const result2 = apply(increment, 2); 45 + console.log({ result2 }); 46 + ``` 47 + 48 + And you can even return a function from your function! 49 + 50 + This is called "HoF" or "Higher order function": 51 + 52 + > Higher order function: a function that receives or returns a function 53 + 54 + ```js 55 + const map = fn => array => array.map(fn); 56 + const incrementAll = map(x => x + 1); 57 + const result = incrementAll([1, 2, 3]); 58 + console.log(result); 59 + ``` 60 + 61 + The `map` method that recieves the function and then the data, is called _currying_ or _partial application_. It is also 62 + called point-free, because after we make this function we can use `map` without using `.map`, and with the right 63 + modifications it can work on _any_ type (`NodeList`!) 64 + 65 + There are libraries to use currying without headaches, like `Ramda`. `Ramda`'s `curry` function gets one argument - a 66 + function with some arity, and understands how many arguments left to provide to the given function on any invocation. 67 + 68 + > ProTip: When using curried functions, we're most likely to take the data as the last argument. 69 + 70 + ```js 71 + const { curry } = require("ramda"); 72 + const map = curry((fn, array) => array.map(fn)); 73 + const result1 = map(x => x + 1, [1, 2, 3]); 74 + console.log({ result1 }); 75 + 76 + const incrementAll = map(x => x + 1); 77 + const result2 = incrementAll([1, 2, 3]); 78 + console.log({ result2 }); 79 + ``` 80 + 81 + For React developers, partial applications and higher order functions should sound very odd. `Redux` is doing it too. 82 + When using `redux`, you use the `connect` function, that has the signature `connect :: config -> Component -> 83 + ConnectedComponent` - gets a config, then a React component, and provides you a new component that may or may not use 84 + your original component.
+41
talk01/02_purity.md
··· 1 + When we talk about "function purity", it means one thing and one thing only: Every time that we call the function with 2 + the same arguments, the same result comes out. That means - no side effects. No DB calls, no exceptions, no mutations of 3 + any kind outside the function scope. 4 + 5 + ```js 6 + const pure = x => x + 1; 7 + const pure = x => Promise.resolve(x); 8 + const impure = x => db.getValueFor(x); 9 + 10 + const pure = xs => xs.map(x => x + 1); 11 + const pure = xs => { 12 + const result = []; 13 + xs.forEach(x => result.push(x + 1)); 14 + return results; // mutation but inside this scope 15 + }; 16 + const impure = xs => 17 + xs.forEach(x => { 18 + x.y += "hey"; // mutation! 19 + }); 20 + ``` 21 + 22 + Think about `React.Component#render`. You can't make HTTP calls there, you can't do anything but to provide the 23 + components based on `this.state` and `this.props`. This is the restriction React's API have to maintain your DOM tree. 24 + Mutations are fine! _React itself does mutations and side effects_, but these should be VERY managed and well tested. 25 + 26 + This is why I love 2 ways of making HTTP calls in React - HoCs and Render props: 27 + 28 + ```js 29 + const ComponentWithData = withFetch("https://app.keywee.co/...", MyComponent); 30 + 31 + // or 32 + function render() { 33 + return ( 34 + <Fetch url={url} onLoading={() => "Loading"} onError={(error, retry) => "Error!"}> 35 + {value => JSON.stringify(value)} 36 + </Fetch> 37 + ); 38 + } 39 + ``` 40 + 41 + But this is for another talk.
+95
talk01/03_lenses.md
··· 1 + # Lenses 2 + 3 + Lenses are a pattern in functional programming to "see" the "world". Lenses can show you the world as it is, you can 4 + focus on whatever you want to see, and you can manipulate everything. 5 + 6 + Lenses have 3 methods: `view`, `over` and `set`. 7 + 8 + ```js 9 + const user = { 10 + name: "gal", 11 + nickname: "schniz" 12 + }; 13 + 14 + const { lensProp, view, over, set } = require("ramda"); 15 + const $name = lensProp("name"); 16 + 17 + const userName = view($name, user); 18 + console.log(userName); 19 + ``` 20 + 21 + You can say that it isn't impressive. It's just like using `user.name`. But lenses give more than just that. They're 22 + composable. 23 + 24 + ```js 25 + const user = { 26 + name: "gal", 27 + nickname: "schniz", 28 + hobby: { 29 + name: "talking" 30 + } 31 + }; 32 + 33 + const { lensProp, view, over, set, compose } = require("ramda"); 34 + const $name = lensProp("name"); 35 + const $hobby = lensProp("hobby"); 36 + 37 + const hobbyName = view(compose($hobby, $name), user); 38 + console.log(hobbyName); 39 + ``` 40 + 41 + Still not impressive? yes, still like `user.hobby.name`. Let's see the most powerful thing about lenses. 42 + 43 + ```js 44 + const { lensProp, view, over, set, compose } = require("ramda"); 45 + const $name = lensProp("name"); 46 + const $hobby = lensProp("hobby"); 47 + 48 + const user = { 49 + name: "gal", 50 + nickname: "schniz", 51 + hobby: { 52 + name: "talking", 53 + lovedSince: "forever" 54 + } 55 + }; 56 + 57 + const userHagever = set($name, "hagever", user); 58 + const userDifferentHobby = set(compose($hobby, $name), "javascripting", user); 59 + console.log({ user, userHagever, userDifferentHobby }); 60 + 61 + // in plain javascript, using shallow copying, it would be like 62 + 63 + const userDifferentHobbyJS = Object.assign({}, user, { 64 + hobby: Object.assign({}, user.hobby, { 65 + name: "javascripting" 66 + }) 67 + }); 68 + 69 + console.log({ userDifferentHobbyJS }); 70 + ``` 71 + 72 + Which one is better? 73 + 74 + The last function I need to show you is `over`. Well, `over` is like `set`, but more flexible. You can implement `set` with `over`. 75 + `over` takes a function (instead of a new plain value) to apply on the old value - it is something between `view` and `set`. 76 + 77 + ```js 78 + const { lensProp, view, over, set, compose } = require("ramda"); 79 + const $nickname = lensProp("name"); 80 + 81 + const user = { 82 + name: "gal", 83 + nickname: "schniz", 84 + }; 85 + 86 + const addHandleIfMissing = nickname => nickname.charAt(0) == '@' ? nickname : `@${nickname}` 87 + const userWithHandle = over($nickname, addHandleIfMissing, user) 88 + console.log({ user, userWithHandle }); 89 + 90 + // implementing set is easy - 91 + 92 + const setWithOver = (lens, value, obj) => over(lens, () => value, obj) 93 + const userWithNewNick = setWithOver($nickname, 'new nick', user) 94 + console.log({ user, userWithNewNick }) 95 + ```