fp-tsの動画の内容の文字化 - 1

fp-tsのtutorial動画があるが
中身を文字で見たかったが　それを公開しているところがなかったので書いてみようと思った
※あまりにも長くなったので分割

# 動画
https://youtube.com/@MrFunctor

## Chapter 1: pipe and flow
https://www.youtube.com/watch?v=WsKEIFirdVc&list=PLUMXrUa_EuePN94nJ2hAui5nWDj8RO3lH&index=1

### pipe - [A -> B]

```math
\displaylines{
pipe(A, f) \\
\\
A \xrightarrow{f} B
}
```

```plantuml
card hello as c1
card 5 as c2

c1 -> c2 : ""size""
```


```ts
import { describe, it } from 'vitest'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial ', () => {
  it('pipe - A -> B', () => {
    const size = (s: string) => s.length

    console.log(pipe('hello', size))

    console.log(size('hello'))
  })
})
```

実行結果
```ts
5
5
```

### pipe - [A -> B -> C]

```math
\displaylines{
pipe(A, f, g) \\
\\
A \xrightarrow{f} B \xrightarrow{g} C
}
```

```plantuml
card hello as c1
card 5 as c2
card true as c3

c1 -> c2 : ""size""
c2 -> c3 : ""isAtLeast3""
```

```ts
import { describe, it } from 'vitest'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial ', () => {
  it('pipe - A -> B -> C', () => {
    const size = (s: string) => s.length
    const isAtLeast3 = (n: number) => n >= 3

    console.log(pipe('hello', size, isAtLeast3))
    console.log(size('hello'))
    console.log(isAtLeast3(size('hello')))
  })
})
```

実行結果
```ts
true
5
true
```

### pipe - [A -> B -> C -> D]

```math
\displaylines{
pipe(A, f, g, h) \\
\\
A \xrightarrow{f} B \xrightarrow{g} C \xrightarrow{h} D
}
```

```plantuml
card "_ hello _" as c0
card hello as c1
card 5 as c2
card true as c3

c0 -> c1 : ""trim""
c1 -> c2 : ""size""
c2 -> c3 : ""isAtLeast3""
```

```ts
import { describe, it } from 'vitest'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial ', () => {
  it('pipe - A -> B -> C -> D', () => {
    const trim = (s: string) => s.trim()
    const size = (s: string) => s.length
    const isAtLeast3 = (n: number) => n >= 3

    const value = ' hello '

    console.log(pipe(value, trim, size, isAtLeast3))
    console.log(value)
    console.log(trim(value))
    console.log(size(trim(value)))
    console.log(isAtLeast3(size(trim(value))))
  })
})
```

実行結果
```ts
true
 hello 
hello
5
true
```

### flow - [A -> B -> C -> D]

```math
\displaylines{
flow(f, g, h) \\
\\
A \xrightarrow{f} B \xrightarrow{g} C \xrightarrow{h} D
}
```

```plantuml
card "_ hello _" as c0
card hello as c1
card 5 as c2
card true as c3

c0 -> c1 : ""trim""
c1 -> c2 : ""size""
c2 -> c3 : ""isAtLeast3""
```

```ts
import { describe, it } from 'vitest'
import { flow } from 'fp-ts/lib/function'

describe('fp-ts Tutorial ', () => {
  it('flow - A -> B -> C -> D', () => {
    const trim = (s: string) => s.trim()
    const size = (s: string) => s.length
    const isAtLeast3 = (n: number) => n >= 3

    const isValid = flow(trim, size, isAtLeast3)

    const value = ' hello '

    console.log(isValid(value))
    console.log(value)
    console.log(trim(value))
    console.log(size(trim(value)))
    console.log(isAtLeast3(size(trim(value))))
  })
})
```

実行結果
```ts
true
 hello 
hello
5
true
```





## Chapter 2: Option
https://www.youtube.com/watch?v=Xh05ynb-DAo&list=PLUMXrUa_EuePN94nJ2hAui5nWDj8RO3lH&index=2

### Option\<number>

```math
inverse(x) = \left\{
\begin{array}{ll}
None & (x = 0) \\
\frac{1}{x} & (x \neq 0)
\end{array}
\right.
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'

describe('fp-ts Tutorial', () => {
  it('Option', () => {
    const inverse = (x: number): O.Option<number> => (x === 0 ? O.none : O.some(1 / x))

    console.log(inverse(0))
    console.log(inverse(2))
  })
})
```

実行結果
```ts
{ _tag: 'None' }
{ _tag: 'Some', value: 0.5 }
```

### 従来の書き方

x=0の場合は未定義（エラーが出る）
```math
\displaylines{
inverse(x) = \frac{1}{x}  \\
(x \neq 0)
}
```

```ts
import { describe, it } from 'vitest'

describe('fp-ts Tutorial', () => {
  it('Option', () => {
    const inverse = (x: number): number => {
      if (x === 0) {
        throw Error('Cannot get inverse of 0.')
      }

      return 1 / x
    }

    console.log(inverse(2))
    console.log(inverse(0))
  })
})
```

実行結果
```ts
0.5
```
実行時にエラーが出る
※'Cannot get inverse of 0.'



### O.fold(onNone, onSome)

```math
\displaylines{

inverse(x) = \left\{
\begin{array}{ll}
None & (x = 0) \\
\frac{1}{x} & (x \neq 0)
\end{array}
\right. 

\\

getUiMessageWithInverse(x) = \left\{
\begin{array}{ll}
Cannot \hspace{5pt} get \hspace{5pt} the \hspace{5pt} inverse \hspace{5pt} of \hspace{5pt} \check{x} . & (x = 0) \\
The \hspace{5pt} inverse \hspace{5pt} of \hspace{5pt} \check{x} \hspace{5pt} is \hspace{5pt} \check{inverse(x)}. & (x \neq 0)
\end{array}
\right. 

}
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('Option - O.fold(onNone, onSome)', () => {
    const inverse = (x: number): O.Option<number> => (x === 0 ? O.none : O.some(1 / x))
    const getUiMessageWithInverse = (x: number): string =>
      pipe(
        x,
        inverse,
        O.fold(
          () => `Cannot get the inverse of ${x}.`,
          (ix) => `The inverse of ${x} is ${ix}.`
        )
      )
    console.log(getUiMessageWithInverse(0))
    console.log(getUiMessageWithInverse(2))
  })
})

```

実行結果
```ts
Cannot get the inverse of 0.
The inverse of 2 is 0.5.
```

### Option\<number> - safe

```math
safeInverse(x) = \left\{
\begin{array}{ll}
0 & (x = 0) \\
\frac{1}{x} & (x \neq 0)
\end{array}
\right.
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { identity, pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('Option<number> - safe', () => {
    const inverse = (x: number): O.Option<number> => (x === 0 ? O.none : O.some(1 / x))
    const safeInverse = (x: number): number =>
      pipe(
        x,
        inverse,
        O.fold(() => 0, identity)
      )
    console.log(safeInverse(0))
    console.log(safeInverse(2))
  })
})
```

実行結果
```ts
0
0.5
```

### Option\<number> - safe - O.getOrElse(onNone)

```math
safeInverse(x) = \left\{
\begin{array}{ll}
0 & (x = 0) \\
\frac{1}{x} & (x \neq 0)
\end{array}
\right.
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('Option<number> - safe - O.getOrElse(onNone)', () => {
    const inverse = (x: number): O.Option<number> => (x === 0 ? O.none : O.some(1 / x))
    const safeInverse = (x: number): number =>
      pipe(
        x,
        inverse,
        O.getOrElse(() => 0)
      )
    console.log(safeInverse(0))
    console.log(safeInverse(2))
  })
})
```

実行結果
```ts
0
0.5
```


### 型変更によるエラー

```math
safeInverse(x) = \left\{
\begin{array}{ll}
invalid & (x = 0) \\
\frac{1}{x} & (x \neq 0)
\end{array}
\right.
```

赤い背景のところでエラーが出る
```diff_typescript
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('Option<number> - safe - O.getOrElse(onNone)', () => {
    const inverse = (x: number): O.Option<number> => (x === 0 ? O.none : O.some(1 / x))
    const safeInverse = (x: number) =>
      pipe(
        x,
-        inverse,
        O.getOrElse(() => 'invalid')
      )
    console.log(safeInverse(0))
    console.log(safeInverse(2))
  })
})
```

実行結果
```ts
invalid
0.5
```
※VSCodeなどでエラー表示されるが、実行は可能
※Argument of type '(x: number) => O.Option<number>' is not assignable to parameter of type '(a: number) => Option<string>'.

### Option\<number> - safe - O.getOrElseW(onNone)

```math
safeInverse(x) = \left\{
\begin{array}{ll}
invalid & (x = 0) \\
\frac{1}{x} & (x \neq 0)
\end{array}
\right.
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('Option<number> - safe - O.getOrElse(onNone)', () => {
    const inverse = (x: number): O.Option<number> => (x === 0 ? O.none : O.some(1 / x))
    const safeInverse = (x: number) =>
      pipe(
        x,
        inverse,
        O.getOrElseW(() => 'invalid')
      )
    console.log(safeInverse(0))
    console.log(safeInverse(2))
  })
})
```

実行結果
```ts
invalid
0.5
```
※getOrElseW　だとエラー表示なし

### O.fromNullable(nullableValue)

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'

describe('fp-ts Tutorial', () => {
  it('O.fromNullable(nullableValue)', () => {
    const value1 = 3
    const value2 = null
    const value3 = undefined

    console.log(O.fromNullable(value1))
    console.log(O.fromNullable(value2))
    console.log(O.fromNullable(value3))
  })
})
```

実行結果
```ts
{ _tag: 'Some', value: 3 }
{ _tag: 'None' }
{ _tag: 'None' }
```

## Chapter 2.1: Option map, flatten, chain
https://www.youtube.com/watch?v=Nvn27eJslSc&list=PLUMXrUa_EuePN94nJ2hAui5nWDj8RO3lH&index=3

### 配列の先頭取得

```plantuml
node 配列 as n1
card 5 as n2
card 6 as n3
card 7 as n4
title [5, 6, 7]
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "[5, 6, 7]" as c1
card 5 as c2

c1 -> c2 : ""head""
```

<br>

```plantuml
node 配列 as n1
title [ _ ] 
```

```plantuml
card "[]" as c1
card None as c2

c1 -> c2 : ""head""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'

describe('fp-ts Tutorial', () => {
  it('Option - head', () => {
    const head = <A>(as: ReadonlyArray<A>): O.Option<A> =>
      as.length === 0 ? O.none : O.some(as[0])

    console.log(head([5, 6, 7]))
    console.log(head([]))
  })
})
```

実行結果
```ts
{ _tag: 'Some', value: 5 }
{ _tag: 'None' }
```

### O.map(f)

```plantuml
node 配列 as n1
card "'An American in Rome'" as n2
card "'Winter Holidays'" as n3
title ['An American in Rome', 'Winter Holidays']
n1 -- n2
n1 -- n3
```

```plantuml
card "['An American in Rome', 'Winter Holidays']" as c1
card "'Best - AN AMERICAN IN ROME'" as c2

c1 -> c2 : ""getBestMovie""
```

<br>

```plantuml
node 配列 as n1
title [ _ ] 
```

```plantuml
card "[]" as c1
card None as c2

c1 -> c2 : ""getBestMovie""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.map(f)', () => {
    const head = <A>(as: ReadonlyArray<A>): O.Option<A> =>
      as.length === 0 ? O.none : O.some(as[0])
    const toUpperCase = (s: string) => s.toUpperCase()
    const addPrefix = (prefix: string) => (s: string) => prefix + s

    const debug_log = <T>(x: T) => {
      console.log(x)
      return x
    }

    const getBestMovie1 = (titles: ReadonlyArray<string>): O.Option<string> =>
      pipe(
        titles,
        debug_log,
        head,
        debug_log,
        O.map((s) => s.toUpperCase()),
        debug_log,
        O.map((s) => `Best - ${s}`)
      )
    const getBestMovie2 = (titles: ReadonlyArray<string>): O.Option<string> =>
      pipe(
        titles,
        debug_log,
        head,
        debug_log,
        O.map(toUpperCase),
        debug_log,
        O.map(addPrefix('Best - '))
      )

    console.log(getBestMovie1(['An American in Rome', 'Winter Holidays']))
    console.log('---------------')
    console.log(getBestMovie1([]))
    console.log('---------------')

    console.log(getBestMovie2(['An American in Rome', 'Winter Holidays']))
    console.log('---------------')
    console.log(getBestMovie2([]))
    console.log('---------------')
  })
})
```

実行結果
```ts
[ 'An American in Rome', 'Winter Holidays' ]
{ _tag: 'Some', value: 'An American in Rome' }
{ _tag: 'Some', value: 'AN AMERICAN IN ROME' }
{ _tag: 'Some', value: 'Best - AN AMERICAN IN ROME' }
---------------
[]
{ _tag: 'None' }
{ _tag: 'None' }
{ _tag: 'None' }
---------------
[ 'An American in Rome', 'Winter Holidays' ]
{ _tag: 'Some', value: 'An American in Rome' }
{ _tag: 'Some', value: 'AN AMERICAN IN ROME' }
{ _tag: 'Some', value: 'Best - AN AMERICAN IN ROME' }
---------------
[]
{ _tag: 'None' }
{ _tag: 'None' }
{ _tag: 'None' }
---------------
```

### 従来のやり方

```plantuml
node 配列 as n1
card "'An American in Rome'" as n2
card "'Winter Holidays'" as n3
title ['An American in Rome', 'Winter Holidays']
n1 -- n2
n1 -- n3
```

```plantuml
card "['An American in Rome', 'Winter Holidays']" as c1
card "'Best - AN AMERICAN IN ROME'" as c2

c1 -> c2 : ""getBestMovie""
```

<br>

```plantuml
node 配列 as n1
title [ _ ] 
```

```plantuml
card "[]" as c1
card None as c2
title 
<color:OrangeRed> 実行時エラーが発生する
end title
c1 -> c2 : ""getBestMovie""
```

```ts
import { describe, it } from 'vitest'

describe('fp-ts Tutorial', () => {
  it('O.map(f)', () => {
    const getBestMovie = (titles: ReadonlyArray<string>): string => {
      const firstTitle = titles[0]

      if (firstTitle === undefined) {
        throw Error('Cannot get the best movie.')
      }

      return `Best 0 ${firstTitle.toLocaleUpperCase()}`
    }

    console.log(getBestMovie(['An American in Rome', 'Winter Holidays']))
    console.log(getBestMovie([]))
  })
})
```

実行結果
```ts
Best 0 AN AMERICAN IN ROME
```
実行時にエラーが出る
※'Cannot get the best movie.'

### O.map(f) - O.some(O.none)

```plantuml
node 配列 as n1
card 0 as n2
card 2 as n3
card 4 as n4
title [0, 2, 4]
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "[0, 2, 4]" as c1
card "O.some(0)" as c2
card "O.some(O.none)" as c3

c1 -> c2 : ""head""
c2 -> c3 : ""O.map(inverse)""
```

<br>

```plantuml
node 配列 as n1
title [ _ ] 
```

```plantuml
card "[]" as c1
card "O.none" as c2
card "O.none" as c3

c1 -> c2 : ""head""
c2 -> c3 : ""O.map(inverse)""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.map(f) - O.some(O.none)', () => {
    const head = <A>(as: ReadonlyArray<A>): O.Option<A> =>
      as.length === 0 ? O.none : O.some(as[0])
    const inverse = (x: number): O.Option<number> => (x === 0 ? O.none : O.some(1 / x))

    const debug_log = <T>(x: T) => {
      console.log(x)
      return x
    }
    const inverseHead = (ns: ReadonlyArray<number>) =>
      pipe(ns, debug_log, head, debug_log, O.map(inverse))

    console.log(inverseHead([0, 2, 4]))
    console.log('------------')
    console.log(inverseHead([]))
    console.log('------------')
  })
})
```

実行結果
```ts
[ 0, 2, 4 ]
{ _tag: 'Some', value: 0 }
{ _tag: 'Some', value: { _tag: 'None' } }
------------
[]
{ _tag: 'None' }
{ _tag: 'None' }
------------
```

### O.map(f) - O.flatten

```plantuml
node 配列 as n1
card 0 as n2
card 2 as n3
card 4 as n4
title [0, 2, 4]
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "[0, 2, 4]" as c1
card "O.some(0)" as c2
card "O.some(O.none)" as c3
card "O.none" as c4

c1 -> c2 : ""head""
c2 -> c3 : ""O.map(inverse)""
c3 -> c4 : ""O.flatten""
```

<br>

```plantuml
node 配列 as n1
card 2 as n2
card 4 as n3
card 6 as n4
title [2, 4, 6]
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "[2, 4, 6]" as c1
card "O.some(2)" as c2
card "O.some(O.some(0.5))" as c3
card "O.some(0.5)" as c4

c1 -> c2 : ""head""
c2 -> c3 : ""O.map(inverse)""
c3 -> c4 : ""O.flatten""
```

<br>

```plantuml
node 配列 as n1
title [ _ ] 
```

```plantuml
card "[]" as c1
card "O.none" as c2
card "O.none" as c3
card "O.none" as c4

c1 -> c2 : ""head""
c2 -> c3 : ""O.map(inverse)""
c3 -> c4 : ""O.flatten""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.map(f) - O.flatten', () => {
    const head = <A>(as: ReadonlyArray<A>): O.Option<A> =>
      as.length === 0 ? O.none : O.some(as[0])
    const inverse = (x: number): O.Option<number> => (x === 0 ? O.none : O.some(1 / x))

    const debug_log = <T>(x: T) => {
      console.log(x)
      return x
    }
    const inverseHead = (ns: ReadonlyArray<number>) =>
      pipe(ns, debug_log, head, debug_log, O.map(inverse), debug_log, O.flatten)

    console.log(inverseHead([0, 2, 4]))
    console.log('------------')
    console.log(inverseHead([2, 4, 6]))
    console.log('------------')
    console.log(inverseHead([]))
    console.log('------------')
  })
})
```

実行結果
```ts
[ 0, 2, 4 ]
{ _tag: 'Some', value: 0 }
{ _tag: 'Some', value: { _tag: 'None' } }
{ _tag: 'None' }
------------
[ 2, 4, 6 ]
{ _tag: 'Some', value: 2 }
{ _tag: 'Some', value: { _tag: 'Some', value: 0.5 } }
{ _tag: 'Some', value: 0.5 }
------------
[]
{ _tag: 'None' }
{ _tag: 'None' }
{ _tag: 'None' }
------------
```

### O.chain(f)

```plantuml
node 配列 as n1
card 0 as n2
card 2 as n3
card 4 as n4
title [0, 2, 4]
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "[0, 2, 4]" as c1
card "O.some(0)" as c2
card "O.none" as c3

c1 -> c2 : ""head""
c2 -> c3 : ""O.chain(inverse)""
```

<br>

```plantuml
node 配列 as n1
card 2 as n2
card 4 as n3
card 6 as n4
title [2, 4, 6]
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "[2, 4, 6]" as c1
card "O.some(2)" as c2
card "O.some(0.5)" as c3

c1 -> c2 : ""head""
c2 -> c3 : ""O.chain(inverse)""
```

<br>

```plantuml
node 配列 as n1
title [ _ ] 
```

```plantuml
card "[]" as c1
card "O.none" as c2
card "O.none" as c3

c1 -> c2 : ""head""
c2 -> c3 : ""O.chain(inverse)""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.chain(f)', () => {
    const head = <A>(as: ReadonlyArray<A>): O.Option<A> =>
      as.length === 0 ? O.none : O.some(as[0])
    const inverse = (x: number): O.Option<number> => (x === 0 ? O.none : O.some(1 / x))

    const debug_log = <T>(x: T) => {
      console.log(x)
      return x
    }
    const inverseHead = (ns: ReadonlyArray<number>) =>
      pipe(ns, debug_log, head, debug_log, O.chain(inverse))

    console.log(inverseHead([0, 2, 4]))
    console.log('------------')
    console.log(inverseHead([2, 4, 6]))
    console.log('------------')
    console.log(inverseHead([]))
    console.log('------------')
  })
})
```

実行結果
```ts
[ 0, 2, 4 ]
{ _tag: 'Some', value: 0 }
{ _tag: 'None' }
------------
[ 2, 4, 6 ]
{ _tag: 'Some', value: 2 }
{ _tag: 'Some', value: 0.5 }
------------
[]
{ _tag: 'None' }
{ _tag: 'None' }
------------
```

## Chapter 2.2: Option fromPredicate
https://www.youtube.com/watch?v=n5hZxN-UwI4&list=PLUMXrUa_EuePN94nJ2hAui5nWDj8RO3lH&index=4

### O.fromPredicate(predicate) - 1

```math
isEven(x) = \left\{
\begin{array}{ll}
true & (x\bmod 2 = 0) \\
false & (x\bmod 2 \neq 0)
\end{array}
\right.
```

```plantuml
card "4" as c1
card "O.some(4)" as c2

c1 -> c2 : ""getEven""
```

<br>

```plantuml
card "5" as c1
card "O.none" as c2

c1 -> c2 : ""getEven""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'

describe('fp-ts Tutorial', () => {
  it('O.fromPredicate(predicate) - 1', () => {
    const isEven = (a: number) => a % 2 === 0
    const getEven = O.fromPredicate(isEven)

    console.log(getEven(4))
    console.log(getEven(5))
  })
})
```

実行結果
```ts
{ _tag: 'Some', value: 4 }
{ _tag: 'None' }
```

### O.fromPredicate(predicate) - 2

```math
isDiscountValid(x) = \left\{
\begin{array}{ll}
true & (x.expired \neq true) \\
false & (x.expired = true)
\end{array}
\right.
```

```plantuml
node オブジェクト as n1
card "percentage" as n2 {
card 10
}
card "expired" as n3 {
card false
}
title "{ percentage: 10, expired: false }"
n1 -- n2
n1 -- n3
```

```plantuml
card "{ percentage: 10, expired: false }" as c1
card "O.some('10% DISCOUNT!')" as c2

c1 -> c2 : ""getDiscountText""
```

<br>

```plantuml
node オブジェクト as n1
card "percentage" as n2 {
card 20
}
card "expired" as n3 {
card true
}
title "{ percentage: 20, expired: true }"
n1 -- n2
n1 -- n3
```

```plantuml
card "{ percentage: 20, expired: true }" as c1
card "O.none" as c2

c1 -> c2 : ""getDiscountText""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.fromPredicate(predicate) - 2', () => {
    type Discount = Readonly<{
      percentage: number
      expired: boolean
    }>

    const isDiscountValid = (discount: Discount) => !discount.expired

    const debug_log = <T>(x: T) => {
      console.log(x)
      return x
    }

    const getDiscountText = (discount: Discount): O.Option<string> =>
      pipe(
        discount,
        debug_log,
        O.fromPredicate(isDiscountValid),
        debug_log,
        O.map(({ percentage }) => `${percentage}% DISCOUNT!`)
      )

    console.log(getDiscountText({ percentage: 10, expired: false }))
    console.log('---------------')
    console.log(getDiscountText({ percentage: 20, expired: true }))
    console.log('---------------')
  })
})
```

実行結果
```ts
{ percentage: 10, expired: false }
{ _tag: 'Some', value: { percentage: 10, expired: false } }
{ _tag: 'Some', value: '10% DISCOUNT!' }
---------------
{ percentage: 20, expired: true }
{ _tag: 'None' }
{ _tag: 'None' }
---------------
```

### O.fromPredicate(refinement)

```math
isCircle(x) = \left\{
\begin{array}{ll}
true & (x.type = Circle) \\
false & (x.type \neq Circle)
\end{array}
\right.
```

```plantuml
node Circleオブジェクト as n1
card "type" as n2 {
card Circle
}
card "radius" as n3 {
card 3
}
title "{ type: 'Circle', radius: 3 }"
n1 -- n2
n1 -- n3
```

```plantuml
card "{ type: 'Circle', radius: 3 }" as c1
card "O.some({ type: 'Circle', radius: 3 })" as c2

c1 -> c2 : ""getCircle""
```

<br>

```plantuml
node Squareオブジェクト as n1
card "type" as n2 {
card Square
}
card "side" as n3 {
card 4
}
title "{ type: 'Square', side: 4 }"
n1 -- n2
n1 -- n3
```

```plantuml
card "{ type: 'Square', side: 4 }" as c1
card "O.none" as c2

c1 -> c2 : ""getCircle""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'

describe('fp-ts Tutorial', () => {
  it('O.fromPredicate(refinement)', () => {
    interface Circle {
      type: 'Circle'
      radius: number
    }
    interface Square {
      type: 'Square'
      side: number
    }
    type Shape = Circle | Square

    const isCircle = (s: Shape): s is Circle => s.type === 'Circle'

    const getCircle = O.fromPredicate(isCircle)

    console.log(getCircle({ type: 'Circle', radius: 3 }))
    console.log(getCircle({ type: 'Square', side: 4 }))
  })
})
```

実行結果
```ts
{ _tag: 'Some', value: { type: 'Circle', radius: 3 } }
{ _tag: 'None' }
```



## Chapter 2.3: Option error handling
https://www.youtube.com/watch?v=EtbU0KxVEJI&list=PLUMXrUa_EuePN94nJ2hAui5nWDj8RO3lH&index=5

### getMovieAwardHighlight

```plantuml
node movie1_オブジェクト as n1
card "title" as n2 {
card "The Kingdom of Monads"
}
card "releaseYear" as n3 {
card 2023
}
card "ratingPosition" as n4 {
card 1
}
card "award" as n5 {
card "Oscar"
}
title "{ title: 'The Kingdom of Monads', releaseYear: 2023, ratingPosition: 1, award: 'Oscar' }"
n1 -- n2
n1 -- n3
n1 -- n4
n1 -- n5
```

```plantuml
card "Oscar" as c1
card "O.some('Oscar')" as c2
card "O.some('Awarded with: Oscar')" as c3

c1 --> c2 : ""O.fromNullable""
c2 --> c3 : ""O.map((award) => `Awarded with: ${award}`)""
```

<br>

```plantuml
node movie2_オブジェクト as n1
card "title" as n2 {
card "Natural Transformations"
}
card "releaseYear" as n3 {
card 2023
}
card "ratingPosition" as n4 {
card 3
}
title "{ title: 'Natural Transformations', releaseYear: 2023, ratingPosition: 3 }"
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "undefined" as c1
card "O.none" as c2
card "O.none" as c3

c1 --> c2 : ""O.fromNullable""
c2 --> c3 : ""O.map((award) => `Awarded with: ${award}`)""
```

<br>

```plantuml
node movie3_オブジェクト as n1
card "title" as n2 {
card "Fun with for loops"
}
card "releaseYear" as n3 {
card 2023
}
card "ratingPosition" as n4 {
card 74
}
title "{ title: 'Fun with for loops', releaseYear: 2023, ratingPosition: 74 }"
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "undefined" as c1
card "O.none" as c2
card "O.none" as c3

c1 --> c2 : ""O.fromNullable""
c2 --> c3 : ""O.map((award) => `Awarded with: ${award}`)""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.alt(f)', () => {
    interface Movie {
      title: string
      releaseYear: number
      ratingPosition: number
      award?: string
    }

    const debug_log = <T>(x: T) => {
      console.log(x)
      return x
    }

    const getMovieAwardHighlight = (movie: Movie): O.Option<string> =>
      pipe(
        movie.award,
        debug_log,
        O.fromNullable,
        debug_log,
        O.map((award) => `Awarded with: ${award}`)
      )

    const movie1: Movie = {
      title: 'The Kingdom of Monads',
      releaseYear: 2023,
      ratingPosition: 1,
      award: 'Oscar'
    }
    const movie2: Movie = {
      title: 'Natural Transformations',
      releaseYear: 2023,
      ratingPosition: 3
    }
    const movie3: Movie = {
      title: 'Fun with for loops',
      releaseYear: 2023,
      ratingPosition: 74
    }

    console.log(getMovieAwardHighlight(movie1))
    console.log('---------------')
    console.log(getMovieAwardHighlight(movie2))
    console.log('---------------')
    console.log(getMovieAwardHighlight(movie3))
    console.log('---------------')
  })
})
```

実行結果
```ts
Oscar
{ _tag: 'Some', value: 'Oscar' }
{ _tag: 'Some', value: 'Awarded with: Oscar' }
---------------
undefined
{ _tag: 'None' }
{ _tag: 'None' }
---------------
undefined
{ _tag: 'None' }
{ _tag: 'None' }
---------------
```

### getMovieTop10Highlight

```plantuml
node movie1_オブジェクト as n1
card "title" as n2 {
card "The Kingdom of Monads"
}
card "releaseYear" as n3 {
card 2023
}
card "ratingPosition" as n4 {
card 1
}
card "award" as n5 {
card "Oscar"
}
title "{ title: 'The Kingdom of Monads', releaseYear: 2023, ratingPosition: 1, award: 'Oscar' }"
n1 -- n2
n1 -- n3
n1 -- n4
n1 -- n5
```

```plantuml
card "movie1" as c1
card "O.some('movie1')" as c2
card "O.some('In TOP 10 at position: 1')" as c3

c1 --> c2 : ""O.fromPredicate(({ ratingPosition }) => ratingPosition <= 10)""
c2 --> c3 : ""O.map(({ ratingPosition }) => `In TOP 10 at position: ${ratingPosition}`)""
```

<br>

```plantuml
node movie2_オブジェクト as n1
card "title" as n2 {
card "Natural Transformations"
}
card "releaseYear" as n3 {
card 2023
}
card "ratingPosition" as n4 {
card 3
}
title "{ title: 'Natural Transformations', releaseYear: 2023, ratingPosition: 3 }"
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "movie2" as c1
card "O.some('movie2')" as c2
card "O.some('In TOP 10 at position: 3')" as c3

c1 --> c2 : ""O.fromPredicate(({ ratingPosition }) => ratingPosition <= 10)""
c2 --> c3 : ""O.map(({ ratingPosition }) => `In TOP 10 at position: ${ratingPosition}`)""
```

<br>

```plantuml
node movie3_オブジェクト as n1
card "title" as n2 {
card "Fun with for loops"
}
card "releaseYear" as n3 {
card 2023
}
card "ratingPosition" as n4 {
card 74
}
title "{ title: 'Fun with for loops', releaseYear: 2023, ratingPosition: 74 }"
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "movie3" as c1
card "O.none" as c2
card "O.none" as c3

c1 --> c2 : ""O.fromPredicate(({ ratingPosition }) => ratingPosition <= 10)""
c2 --> c3 : ""O.map(({ ratingPosition }) => `In TOP 10 at position: ${ratingPosition}`)""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.alt(f)', () => {
    interface Movie {
      title: string
      releaseYear: number
      ratingPosition: number
      award?: string
    }

    const debug_log = <T>(x: T) => {
      console.log(x)
      return x
    }

    const getMovieTop10Highlight = (movie: Movie): O.Option<string> =>
      pipe(
        movie,
        debug_log,
        O.fromPredicate(({ ratingPosition }) => ratingPosition <= 10),
        debug_log,
        O.map(({ ratingPosition }) => `In TOP 10 at position: ${ratingPosition}`)
      )

    const movie1: Movie = {
      title: 'The Kingdom of Monads',
      releaseYear: 2023,
      ratingPosition: 1,
      award: 'Oscar'
    }
    const movie2: Movie = {
      title: 'Natural Transformations',
      releaseYear: 2023,
      ratingPosition: 3
    }
    const movie3: Movie = {
      title: 'Fun with for loops',
      releaseYear: 2023,
      ratingPosition: 74
    }

    console.log(getMovieTop10Highlight(movie1))
    console.log('---------------')
    console.log(getMovieTop10Highlight(movie2))
    console.log('---------------')
    console.log(getMovieTop10Highlight(movie3))
    console.log('---------------')
  })
})
```

実行結果
```ts
{
  title: 'The Kingdom of Monads',
  releaseYear: 2023,
  ratingPosition: 1,
  award: 'Oscar'
}
{
  _tag: 'Some',
  value: {
    title: 'The Kingdom of Monads',
    releaseYear: 2023,
    ratingPosition: 1,
    award: 'Oscar'
  }
}
{ _tag: 'Some', value: 'In TOP 10 at position: 1' }
---------------
{
  title: 'Natural Transformations',
  releaseYear: 2023,
  ratingPosition: 3
}
{
  _tag: 'Some',
  value: {
    title: 'Natural Transformations',
    releaseYear: 2023,
    ratingPosition: 3
  }
}
{ _tag: 'Some', value: 'In TOP 10 at position: 3' }
---------------
{ title: 'Fun with for loops', releaseYear: 2023, ratingPosition: 74 }
{ _tag: 'None' }
{ _tag: 'None' }
---------------
```




### O.alt(f)

```plantuml
node movie1_オブジェクト as n1
card "title" as n2 {
card "The Kingdom of Monads"
}
card "releaseYear" as n3 {
card 2023
}
card "ratingPosition" as n4 {
card 1
}
card "award" as n5 {
card "Oscar"
}
title "{ title: 'The Kingdom of Monads', releaseYear: 2023, ratingPosition: 1, award: 'Oscar' }"
n1 -- n2
n1 -- n3
n1 -- n4
n1 -- n5
```

```plantuml
card "movie1" as c1
card "O.some('Awarded with: Oscar')" as c2
card "O.some('Awarded with: Oscar')" as c3
card "'Awarded with: Oscar'" as c4

c1 --> c2 : ""getMovieAwardHighlight""
c2 --> c3 : ""O.alt(() => getMovieTop10Highlight(movie))""
c3 --> c4 : ""O.getOrElse(() => `Released in ${movie.releaseYear}`)""
```

<br>

```plantuml
node movie2_オブジェクト as n1
card "title" as n2 {
card "Natural Transformations"
}
card "releaseYear" as n3 {
card 2023
}
card "ratingPosition" as n4 {
card 3
}
title "{ title: 'Natural Transformations', releaseYear: 2023, ratingPosition: 3 }"
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "movie2" as c1
card "O.none" as c2
card "O.some('In TOP 10 at position: 3')" as c3
card "'In TOP 10 at position: 3'" as c4

c1 --> c2 : ""getMovieAwardHighlight""
c2 --> c3 : ""O.alt(() => getMovieTop10Highlight(movie))""
c3 --> c4 : ""O.getOrElse(() => `Released in ${movie.releaseYear}`)""
```

<br>

```plantuml
node movie3_オブジェクト as n1
card "title" as n2 {
card "Fun with for loops"
}
card "releaseYear" as n3 {
card 2023
}
card "ratingPosition" as n4 {
card 74
}
title "{ title: 'Fun with for loops', releaseYear: 2023, ratingPosition: 74 }"
n1 -- n2
n1 -- n3
n1 -- n4
```

```plantuml
card "movie3" as c1
card "O.none" as c2
card "O.none" as c3
card "'IReleased in 2023'" as c4

c1 --> c2 : ""getMovieAwardHighlight""
c2 --> c3 : ""O.alt(() => getMovieTop10Highlight(movie))""
c3 --> c4 : ""O.getOrElse(() => `Released in ${movie.releaseYear}`)""
```

```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.alt(f)', () => {
    interface Movie {
      title: string
      releaseYear: number
      ratingPosition: number
      award?: string
    }

    const debug_log = <T>(x: T) => {
      console.log(x)
      return x
    }

    const getMovieAwardHighlight = (movie: Movie): O.Option<string> =>
      pipe(
        movie.award,
        // debug_log,
        O.fromNullable,
        // debug_log,
        O.map((award) => `Awarded with: ${award}`)
      )
    const getMovieTop10Highlight = (movie: Movie): O.Option<string> =>
      pipe(
        movie,
        // debug_log,
        O.fromPredicate(({ ratingPosition }) => ratingPosition <= 10),
        // debug_log,
        O.map(({ ratingPosition }) => `In TOP 10 at position: ${ratingPosition}`)
      )

    const getMovieHighlight = (movie: Movie): string =>
      pipe(
        movie,
        debug_log,
        getMovieAwardHighlight,
        debug_log,
        O.alt(() => getMovieTop10Highlight(movie)),
        debug_log,
        O.getOrElse(() => `Released in ${movie.releaseYear}`)
      )

    const movie1: Movie = {
      title: 'The Kingdom of Monads',
      releaseYear: 2023,
      ratingPosition: 1,
      award: 'Oscar'
    }
    const movie2: Movie = {
      title: 'Natural Transformations',
      releaseYear: 2023,
      ratingPosition: 3
    }
    const movie3: Movie = {
      title: 'Fun with for loops',
      releaseYear: 2023,
      ratingPosition: 74
    }

    console.log(getMovieHighlight(movie1))
    console.log('---------------')
    console.log(getMovieHighlight(movie2))
    console.log('---------------')
    console.log(getMovieHighlight(movie3))
    console.log('---------------')
  })
})
```

実行結果
```ts
{
  title: 'The Kingdom of Monads',
  releaseYear: 2023,
  ratingPosition: 1,
  award: 'Oscar'
}
{ _tag: 'Some', value: 'Awarded with: Oscar' }
{ _tag: 'Some', value: 'Awarded with: Oscar' }
Awarded with: Oscar
---------------
{
  title: 'Natural Transformations',
  releaseYear: 2023,
  ratingPosition: 3
}
{ _tag: 'None' }
{ _tag: 'Some', value: 'In TOP 10 at position: 3' }
In TOP 10 at position: 3
---------------
```

### O.altW(f) - W means Widen

https://gcanti.github.io/fp-ts/guides/code-conventions.html#what-a-w-suffix-means-eg-chainw-or-chaineitherkw

赤い背景のところでエラーが出る
```diff_typescript
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.altW(f)', () => {
    const f = (a: O.Option<number>) =>
      pipe(
-        a,
        O.alt(() => O.some('invalid'))
      )

    console.log(f(O.of(3)))
    console.log(f(O.none))
  })
})
```

実行結果
```ts
{ _tag: 'Some', value: 3 }
{ _tag: 'Some', value: 'invalid' }
```
※VSCodeなどでエラー表示されるが、実行は可能
※Argument of type 'Option<number>' is not assignable to parameter of type 'Option<string>'.

O.altW　ならエラー表示なし
```ts
import { describe, it } from 'vitest'
import * as O from 'fp-ts/Option'
import { pipe } from 'fp-ts/lib/function'

describe('fp-ts Tutorial', () => {
  it('O.altW(f)', () => {
    const f = (a: O.Option<number>) =>
      pipe(
        a,
        O.altW(() => O.some('invalid'))
      )

    console.log(f(O.of(3)))
    console.log(f(O.none))
  })
})
```

実行結果
```ts
{ _tag: 'Some', value: 3 }
{ _tag: 'Some', value: 'invalid' }
```