https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript
아래 책으로 유명한 저자의 라이브 코딩 무료 강의이다.
아래 책은 아직 보진 않았지만, 위 동영상의 내용이 좋아 나중에 한번 봐야겠다.
https://github.com/MostlyAdequate/mostly-adequate-guide
https://mostly-adequate.gitbook.io/mostly-adequate-guide/
추가로 저자의 유료강의도 있다. 저자는 Salesforce의 개발자다.
그렇게 비싸지는 않아 영어실력이 있으면 한번 보는것도 추천한다.
https://www.pluralsight.com/courses/hardcore-functional-programming-javascript
선언적 코드를 박스타입으로 리팩터링
- 중첩구조의 평면화
- 연산, 정규화, 타입변환, 메서드호출, 함수호출을 map, fold로 단순화
Either 타입으로 null 체크 로직을 composable하게
- 체인과 폴드는 타입이 없으면 비슷하게 생김.
- 폴드는 박스에서 값을 제거하는 개념
- 체인은 박스에서 값을 가져와 함수를 적용하는해 다시 박스를 리턴하는 개념
- 정의는 비슷할 수 있지만 두가지 매우 다른 개념.
- 코드 브랜칭, 널체크는 disjunction(or) 개념을 캡쳐함.
- fromNullable
- fold(f,g)
- https://embed.plnkr.co/xvHsMY3qTdcDJ0rAMyz9?show=script.js,preview
- tryCatch
Either를 이용한 리팩터링
코드 주소가 없어서 화면 복붙
Semigroup으로 type 만들기
- semigroup은 concat 메서드를 갖고 있는 타입
- 배열
- 스트링
- 결합 법칙이 성립
- 새로운 세미그룹 만들기
- 불리언 ALL
- Fist
https://embed.plnkr.co/DOEGK6vi1ChEC2wvmtH2?show=script.js,preview
- 레코드 데이터 타입 결합(concat)하기
- property가 semigroup인 product type
- https://embed.plnkr.co/Kt1riQvnI5pxrRQ4rTzj/?show=src%2Flib%2Fscript.js,preview
monoid로 실패에 안전한 함수 합성
- 항등원의 도입
- 리스트에 모노이드 연산 적용
- 레코드의 속성을 모노이드로
- 레코드의 속성이 모두 모노이드면 fold가능
- https://embed.plnkr.co/exD0nndaoENlWysBBT9e?show=script.js,preview
const util = require("util");
import { List } from "immutable-ext";
const fromNullable = x => (x != null ? Right(x) : Left(null));
// Example 1
const Sum = x => ({
x,
concat: ({ x: y }) => Sum(x + y),
[util.inspect.custom]: () => `Sum(${x})`
});
Sum.empty = () => Sum(0);
// Example 2
const Product = x => ({
x,
concat: ({ x: y }) => Product(x * y),
[util.inspect.custom]: () => `Product(${x})`
});
Product.empty = () => Product(1);
// Example 3
const Any = x => ({
x,
concat: ({ x: y }) => Any(x || y),
[util.inspect.custom]: () => `Any(${x})`
});
Any.empty = () => Any(false);
// Example 4
const All = x => ({
x,
concat: ({ x: y }) => All(x && y),
[util.inspect.custom]: () => `All(${x})`
});
All.empty = () => All(true);
// Example 5
const Max = x => ({
x,
concat: ({ x: y }) => Max(x > y ? x : y),
[util.inspect.custom]: () => `Max(${x})`
});
Max.empty = () => Max(-Infinity);
// Example 6
const Min = x => ({
x,
concat: ({ x: y }) => Min(x < y ? x : y),
[util.inspect.custom]: () => `Min(${x})`
});
Min.empty = () => Min(Infinity);
// Example 7
const Right = x => ({
chain: f => f(x),
map: f => Right(f(x)),
fold: (f, g) => g(x),
concat: o => o.fold(e => Left(e), r => Right(x.concat(r))),
[util.inspect.custom]: () => `Right(${x})`
});
const Left = x => ({
chain: f => Left(x),
map: f => Left(x),
fold: (f, g) => f(x),
concat: o => Left(x),
[util.inspect.custom]: () => `Left(${x})`
});
const stats = List.of(
{ page: "Home", views: 40 },
{ page: "About", views: 10 },
{ page: "Blog", views: null }
);
stats.foldMap(x => fromNullable(x.views).map(Sum), Right(Sum(0)));
// Left(null)
// Example 8
const First = either => ({
fold: f => f(either),
concat: o => (either.isLeft ? o : First(either)),
[util.inspect.custom]: () => `Min(${x})`
});
First.empty = () => First(Left());
const find = (xs, f) =>
List(xs)
.foldMap(x => First(f(x) ? Right(x) : Left()), First.empty())
.fold(x => x);
find([3, 4, 5, 6, 7], x => x > 4);
// Right(5)
// Example 9
const Fn = f => ({ fold: f, concat: o => Fn(x => f(x).concat(o.fold(x))) });
const hasVowels = x => !!x.match(/[aeiou]/gi);
const longWord = x => x.length >= 5;
const both = Fn(
// All(hasVowels(x))
compose(
All,
hasVowels
)
).concat(
Fn(
// All(longWord(x))
compose(
All,
longWord
)
)
);
[("gym", "bird", "lilac")].filter(x => both.fold(x).x);
// [lilac]
// Example 10
const Pair = (x, y) => ({
x,
y,
concat: ({ x: x1, y: y1 }) => Pair(x.concat(x1), y.concat(y1)),
[util.inspect.custom]: () => `Pair(${x}, ${y})`
});
foldMap으로 타입 언박싱
매핑 후 폴딩
- 레코드의 속성, 리스트의 값을 모노이드로 매핑 후 폴드할 수 있음.
- 레코드 타입의 속성들을 모노이드로 생각하자
https://embed.plnkr.co/jpjMXRd2Hz8zIBLEM0Q4?show=script.js,preview
LazyBox로 지연평가
thunk를 이용하여 코드의 평가를 늦춤
fold가 코드 실행을 트리거
프로미스, 옵저버블 스트림과 같은 구체적인 값 대신 내부에 함수가 있습니다.
Task로 부수효과 캡처
예제는 Folktale 라이브러리 사용
Either랑 다르게 부수효과가 존재함을 보여줌.
Task로 비동기 액션 캡처
파일 읽기/쓰기, HTTP통신, 로깅 등 온갖 사이드이펙트를 격리
당신은 Functor를 사용하고 있었다.
합성 보존과 Identity
합성 순서를 주의한다.
fx.map(f).map(g) === fx.map(x=>g(f(x))
const id = x =>x
fx.map(id) === id(fx)
of를 이용해 Pointed Functor로 Lift
Either의 초기값이 Right인 이유는?
초기값이 Left면 함수 합성이 무시됨.
직관은 성공했다 가정하고 연산을 예약하는 것임.
당신은 Monad를 사용하고 있었다.
- F.of
- F.chain (flatMap, bind, >>=)
체인의 특징은 두개의 중첩 타입을 flatten 한다는 것임.
항등원과 결합법칙 성립
모든 모나드에서 map 메서드를 파생할 수 있습니다.
이것은 모나드가 functor라는 것을 의미합니다.
또한 모나드는 applicative functor면서 pointed functor입니다.
즉, 모나드를 이용해 모든 다른 메서드를 정의할 수 있습니다.
하지만 가장 중요한 기능은 두 가지 타입을 결합하는 것입니다. (chain)
m.chain(x=>M.of(f(x)))
많은 사람들이 M 단어를 두려워합니다.
나는 모나드가 chainable이라고 부르를 수 있을 만큼 쉬운 것이라고 믿습니다.
본질을 꿰뚫는 말입니다.
사실, 범주 이론에 대한 수년간의 수학적 연구는
모나드가 어떻게 작동하는지,
모나드가 보유하는 속성,
어떻게 상호 작용하는지에 대해 설명했습니다.
Build Curried functions
딱히 설명할 게 없어보임. 다인자 함수을 일인자 함수의 연속으로
Applicative Functors for multiple arguments
드디어 다시 코드가 제공됨
펑터 법칙을 만족함
F(x).map(f) === F(f).ap(F(x))
lift 형태를 사용하면 add(1,2)랑 모양이 비슷해서 이해가 쉬움
Apply multiple functors as arguments to a function (Applicatives)
중첨된 코드를 리팩토링 해보자
const $ = selector => Either.of({selector,height:10})
const getScreenSize = screen => head => foot => screen - (head.height + foot.height)
const res3 = $('header').chain(head=>$('footer').map(foot=>getScreenSize(800)(head)(foot)))
console.log("result 3 ",res3);
const res4 = Either.of(getScreenSize(800)).ap($('header')).ap($('footer'));
console.log("result 4 ",res4);
List comprehensions with Applicative Functors
외부 라이브러리를 사용해야 함.
파이썬 등 다른 언어에서 많이 볼 수 있는 친구
Write applicatives for concurrent actions
이것도 구현은 외부 라이브러리를 사용한다.
아래는 프로미스 헬과 동일한 형태이다.
프로미스 헬 해결과 동시 실행 효과를 얻을 수 있다.
Leapfrogging types with Traversable
[Task] => Task([]) 형태로 바꾸는 것 (Promise. all)
Task.of를 통해 바깥 타입을 지정해준다.
Maintaining structure whilst asyncing
Principled type conversions with Natural Transformations
자연 변환은 단순하게 말하면 타입 변환임
한 펑터에서 다른 펑터로....
nt(x).map(f) == nt(x.map(f) 규칙을 만족해야 함.
F a -> G a
Apply Natural Transformations in everyday work
Array => List
array=>Right
또다른 예제 : 타입 중첩 > 트랜스포머로 해결
Isomorphisms and round trip data transformations
isomorphism ??
이것은 흥미로운 관계입니다.
이것이 의미하는 바는 이러한 함수가 우리의 데이터 타입이 다른 데이터 타입과 동일한 정보를 보유하고 있음을 증명한다는 것입니다.
나는 문자열이 문자 배열과 동형이라고 주장합니다.
이 두 데이터 타입은 동일한 정보를 보유해야 하며 손실 없이 변환할 수 있어야 합니다.
스트링은 모든 배열과 isomorpic하지 않음, 캐릭터 배열과만 동형임
또다른 예제
Build a data flow for a real world app
다음 두 강의랑 이어지는 내용임
스포티파이 api에서 두 아티스트의 공통점을 찾기 위한 계획을 세웁니다.
필요한 데이터를을 확보할 수 있도록 데이터 흐름을 스케치합니다.
Retrieve and use data from an api with pure functional constructs
Task로 되어있으나 Promise를 사용할 수 있음.
- http 요청을 통해 Promise (resolved, rejected)를 가져옴
- then 체인을 통해 map함
- then / catch 양쪽에서 모나드 트랜스포머 사용
- catch면 그대로 task
- then이면 이전에 then()을 통해 포함한 Either를 확인하여 reject할 것인지, resolve할 것인지 결정.
2. https://github.com/DrBoolean/spotify-fp-example/tree/master
Find the intersection of sets with Semigroups