LoginSignup
0
1

fp-ts Functorの内容確認

Last updated at Posted at 2024-05-09

fp-tsの一覧ページがあまりにも重くなったので、分割

参照元

一覧

Functorの内容確認

参考

ファイル名 import先 ソースの中身 declare module がある importでの参照される数
Functor
  • function
  • HKT
interface, function のみ なし 12

満たすべきもの

恒等射、単位元

F.map(fa, x => x) = fa

射の合成、合成

F.map(fa, x => f(g(x))) = F.map(F.map(fa, g), f)

import { describe, it } from 'vitest'
import { type Functor1 } from 'fp-ts/lib/Functor'
import type { Kind, URIS } from 'fp-ts/lib/HKT'
import { pipe } from 'fp-ts/lib/function'
import * as f from 'fp-ts/function'

const URI = 'Test2'
type URI = typeof URI

declare module 'fp-ts/HKT' {
  interface URItoKind<A> {
    readonly Test2: Test2<A>
  }
}

const Test2Functor: Functor1<URI> = {
  URI,
  map: (ma, f) => ({ Test2: f(ma.Test2) })
}

const Functor1Id: <F extends URIS>(Functor: Functor1<F>) => <A>(fa: Kind<F, A>) => Kind<F, A> =
  (Functor) => (fa) =>
    Functor.map(fa, f.identity)

const Functor1Composition1: <F extends URIS>(
  Functor: Functor1<F>
) => <A, B>(g: (a: A) => B) => <C>(f: (a: B) => C) => (fa: Kind<F, A>) => Kind<F, C> =
  (Functor) => (g) => (f) => (fa) =>
    Functor.map(fa, (x) => f(g(x)))

const Functor1Composition2: <F extends URIS>(
  Functor: Functor1<F>
) => <A, B>(g: (a: A) => B) => <C>(f: (a: B) => C) => (fa: Kind<F, A>) => Kind<F, C> =
  (Functor) => (g) => (f) => (fa) =>
    Functor.map(Functor.map(fa, g), f)

type Test2<XXX> = {
  Test2: XXX
}

type A = number
type B = string
type C = boolean

type A2 = string
type B2 = boolean
type C2 = number

type A3 = boolean
type B3 = string
type C3 = number

type F<XXX> = Test2<XXX>

describe('fp-ts Tutorial', () => {
  it('Functor', () => {
    // object
    const a: A = 123
    const a2: A2 = 'abc'
    const a3: A3 = true

    const fa: F<A> = {
      Test2: a
    }
    const fa2: F<A2> = {
      Test2: a2
    }
    const fa3: F<A3> = {
      Test2: a3
    }

    const a_b_function: (a: A) => B = (a) => String(a)
    const b_c_function: (b: B) => C = (b) => Boolean(b)

    const a2_b2_function: (a: A2) => B2 = (a) => Boolean(a)
    const b2_c2_function: (b: B2) => C2 = (b) => (Number.isNaN(Number(b)) ? 0 : Number(b))

    const a3_b3_function: (a: A3) => B3 = (a) => String(a)
    const b3_c3_function: (b: B3) => C3 = (b) => (Number.isNaN(Number(b)) ? 0 : Number(b))

    console.log('A => B => C')
    console.log()
    console.log('F.map(fa, x => x) - ', pipe(fa, Functor1Id(Test2Functor)))
    console.log('fa - ', fa)
    console.log()
    console.log(
      'F.map(fa, x => f(g(x))) - ',
      pipe(fa, Functor1Composition1(Test2Functor)(a_b_function)(b_c_function))
    )
    console.log(
      'F.map(F.map(fa, g), f) - ',
      pipe(fa, Functor1Composition2(Test2Functor)(a_b_function)(b_c_function))
    )

    console.log('---------')

    console.log('A2 => B2 => C2')
    console.log()
    console.log('F.map(fa, x => x) - ', pipe(fa2, Functor1Id(Test2Functor)))
    console.log('fa - ', fa2)
    console.log()
    console.log(
      'F.map(fa, x => f(g(x))) - ',
      pipe(fa2, Functor1Composition1(Test2Functor)(a2_b2_function)(b2_c2_function))
    )
    console.log(
      'F.map(F.map(fa, g), f) - ',
      pipe(fa2, Functor1Composition2(Test2Functor)(a2_b2_function)(b2_c2_function))
    )

    console.log('---------')

    console.log('A3 => B3 => C3')
    console.log()
    console.log('F.map(fa, x => x) - ', pipe(fa3, Functor1Id(Test2Functor)))
    console.log('fa - ', fa3)
    console.log()
    console.log(
      'F.map(fa, x => f(g(x))) - ',
      pipe(fa3, Functor1Composition1(Test2Functor)(a3_b3_function)(b3_c3_function))
    )
    console.log(
      'F.map(F.map(fa, g), f) - ',
      pipe(fa3, Functor1Composition2(Test2Functor)(a3_b3_function)(b3_c3_function))
    )
  })
})

実行結果

A => B => C

F.map(fa, x => x) -  { Test2: 123 }
fa -  { Test2: 123 }

F.map(fa, x => f(g(x))) -  { Test2: true }
F.map(F.map(fa, g), f) -  { Test2: true }
---------
A2 => B2 => C2

F.map(fa, x => x) -  { Test2: 'abc' }
fa -  { Test2: 'abc' }

F.map(fa, x => f(g(x))) -  { Test2: 1 }
F.map(F.map(fa, g), f) -  { Test2: 1 }
---------
A3 => B3 => C3

F.map(fa, x => x) -  { Test2: true }
fa -  { Test2: true }

F.map(fa, x => f(g(x))) -  { Test2: 0 }
F.map(F.map(fa, g), f) -  { Test2: 0 }

Functorの法則を満たさないもの

不完全なFunctorを作成できてしまう

import { describe, it } from 'vitest'
import { type Functor1 } from 'fp-ts/lib/Functor'
import type { Kind, URIS } from 'fp-ts/lib/HKT'
import { pipe } from 'fp-ts/lib/function'
import * as f from 'fp-ts/function'

const URI = 'Test2'
type URI = typeof URI

declare module 'fp-ts/HKT' {
  interface URItoKind<A> {
    readonly Test2: Test2<A>
  }
}

const Test2Functor: Functor1<URI> = {
  URI,
  map: <A, B>(ma: Kind<URI, A>, f: (a: A) => B) => {
    const test: A =
      typeof ma.Test2 === 'number'
        ? ((ma.Test2 + 1) as A)
        : typeof ma.Test2 === 'string'
          ? ((ma.Test2 + 'a') as A)
          : typeof ma.Test2 === 'boolean'
            ? (!ma.Test2 as A)
            : ma.Test2
    return { Test2: f(test) }
  }
}

const Functor1Id: <F extends URIS>(Functor: Functor1<F>) => <A>(fa: Kind<F, A>) => Kind<F, A> =
  (Functor) => (fa) =>
    Functor.map(fa, f.identity)

const Functor1Composition1: <F extends URIS>(
  Functor: Functor1<F>
) => <A, B>(g: (a: A) => B) => <C>(f: (a: B) => C) => (fa: Kind<F, A>) => Kind<F, C> =
  (Functor) => (g) => (f) => (fa) =>
    Functor.map(fa, (x) => f(g(x)))

const Functor1Composition2: <F extends URIS>(
  Functor: Functor1<F>
) => <A, B>(g: (a: A) => B) => <C>(f: (a: B) => C) => (fa: Kind<F, A>) => Kind<F, C> =
  (Functor) => (g) => (f) => (fa) =>
    Functor.map(Functor.map(fa, g), f)

type Test2<XXX> = {
  Test2: XXX
}

type A = number
type B = string
type C = boolean

type A2 = string
type B2 = boolean
type C2 = number

type A3 = boolean
type B3 = string
type C3 = number

type F<XXX> = Test2<XXX>

describe('fp-ts Tutorial', () => {
  it('Functor', () => {
    // object
    const a: A = 123
    const a2: A2 = 'abc'
    const a3: A3 = true

    const fa: F<A> = {
      Test2: a
    }
    const fa2: F<A2> = {
      Test2: a2
    }
    const fa3: F<A3> = {
      Test2: a3
    }

    const a_b_function: (a: A) => B = (a) => String(a)
    const b_c_function: (b: B) => C = (b) => Boolean(b)

    const a2_b2_function: (a: A2) => B2 = (a) => Boolean(a)
    const b2_c2_function: (b: B2) => C2 = (b) => (Number.isNaN(Number(b)) ? 0 : Number(b))

    const a3_b3_function: (a: A3) => B3 = (a) => String(a)
    const b3_c3_function: (b: B3) => C3 = (b) => (Number.isNaN(Number(b)) ? 0 : Number(b))

    console.log('A => B => C')
    console.log()
    console.log('F.map(fa, x => x) - ', pipe(fa, Functor1Id(Test2Functor)))
    console.log('fa - ', fa)
    console.log()
    console.log(
      'F.map(fa, x => f(g(x))) - ',
      pipe(fa, Functor1Composition1(Test2Functor)(a_b_function)(b_c_function))
    )
    console.log(
      'F.map(F.map(fa, g), f) - ',
      pipe(fa, Functor1Composition2(Test2Functor)(a_b_function)(b_c_function))
    )

    console.log('---------')

    console.log('A2 => B2 => C2')
    console.log()
    console.log('F.map(fa, x => x) - ', pipe(fa2, Functor1Id(Test2Functor)))
    console.log('fa - ', fa2)
    console.log()
    console.log(
      'F.map(fa, x => f(g(x))) - ',
      pipe(fa2, Functor1Composition1(Test2Functor)(a2_b2_function)(b2_c2_function))
    )
    console.log(
      'F.map(F.map(fa, g), f) - ',
      pipe(fa2, Functor1Composition2(Test2Functor)(a2_b2_function)(b2_c2_function))
    )

    console.log('---------')

    console.log('A3 => B3 => C3')
    console.log()
    console.log('F.map(fa, x => x) - ', pipe(fa3, Functor1Id(Test2Functor)))
    console.log('fa - ', fa3)
    console.log()
    console.log(
      'F.map(fa, x => f(g(x))) - ',
      pipe(fa3, Functor1Composition1(Test2Functor)(a3_b3_function)(b3_c3_function))
    )
    console.log(
      'F.map(F.map(fa, g), f) - ',
      pipe(fa3, Functor1Composition2(Test2Functor)(a3_b3_function)(b3_c3_function))
    )
  })
})

実行結果

A => B => C

F.map(fa, x => x) -  { Test2: 124 }
fa -  { Test2: 123 }

F.map(fa, x => f(g(x))) -  { Test2: true }
F.map(F.map(fa, g), f) -  { Test2: true }
---------
A2 => B2 => C2

F.map(fa, x => x) -  { Test2: 'abca' }
fa -  { Test2: 'abc' }

F.map(fa, x => f(g(x))) -  { Test2: 1 }
F.map(F.map(fa, g), f) -  { Test2: 0 }
---------
A3 => B3 => C3

F.map(fa, x => x) -  { Test2: false }
fa -  { Test2: true }

F.map(fa, x => f(g(x))) -  { Test2: 0 }
F.map(F.map(fa, g), f) -  { Test2: 0 }

Functorの利用

import { describe, it } from 'vitest'
import { type Functor1 } from 'fp-ts/lib/Functor'
// import type { Kind, URIS } from 'fp-ts/lib/HKT'
import { pipe } from 'fp-ts/lib/function'
// import * as f from 'fp-ts/function'
import * as F from 'fp-ts/Functor'

const URI2 = 'Test2'
type URI2 = typeof URI2

const URI3 = 'Test3'
type URI3 = typeof URI3

type Test2<XXX> = {
  Test2: XXX
}

type Test3<XXX> = {
  Test3: XXX[]
}

declare module 'fp-ts/HKT' {
  interface URItoKind<A> {
    readonly Test2: Test2<A>
    readonly Test3: Test3<A>
  }
}

const Test2Functor: Functor1<URI2> = {
  URI: URI2,
  map: (ma, f) => ({ Test2: f(ma.Test2) })
}

const Test3Functor: Functor1<URI3> = {
  URI: URI3,
  map: (ma, f) => ({ Test3: ma.Test3.map(f) })
}

// const Functor1Id: <FFF extends URIS>(
//   Functor: Functor1<FFF>
// ) => <XXX>(fa: Kind<FFF, XXX>) => Kind<FFF, XXX> = (Functor) => (fa) => Functor.map(fa, f.identity)

// const Functor1Composition1: <FFF extends URIS>(
//   Functor: Functor1<FFF>
// ) => <XXX, YYY>(
//   g: (x: XXX) => YYY
// ) => <ZZZ>(f: (y: YYY) => ZZZ) => (fx: Kind<FFF, XXX>) => Kind<FFF, ZZZ> =
//   (Functor) => (g) => (f) => (fa) =>
//     Functor.map(fa, (x) => f(g(x)))

// const Functor1Composition2: <FFF extends URIS>(
//   Functor: Functor1<FFF>
// ) => <XXX, YYY>(
//   g: (x: XXX) => YYY
// ) => <ZZZ>(f: (y: YYY) => ZZZ) => (fx: Kind<FFF, XXX>) => Kind<FFF, ZZZ> =
//   (Functor) => (g) => (f) => (fa) =>
//     Functor.map(Functor.map(fa, g), f)

type A = number
type B = string
// type C = boolean

type F2<XXX> = Test2<XXX>
type F3<XXX> = Test3<XXX>
type F<XXX> = Test3<Test2<XXX>>

describe('fp-ts Tutorial', () => {
  it('Functor', () => {
    // object
    const a123: A = 123
    const a456: A = 456
    const a789: A = 789

    const fa2_123: F2<A> = {
      Test2: a123
    }
    const fa2_456: F2<A> = {
      Test2: a456
    }
    const fa2_789: F2<A> = {
      Test2: a789
    }

    const fa3: F3<A> = {
      Test3: [a123, a456, a789]
    }

    const fa: F<A> = {
      Test3: [fa2_123, fa2_456, fa2_789]
    }

    const a_b_function: (a: A) => B = (a) => String(a)
    // const b_c_function: (b: B) => C = (b) => Boolean(b)

    const fa2_function_object: F2<(a: A) => B> = {
      Test2: a_b_function
    }
    const fa3_function_object: F3<(a: A) => B> = {
      Test3: [a_b_function, (a) => a_b_function(2 * a), (a) => a_b_function(3 * a)]
    }

    // // check
    // console.log('---- check -----')

    // console.log('Test2Functor')
    // console.log()
    // console.log('A => B => C')
    // console.log()
    // console.log('F.map(fa, x => x) - ', pipe(fa2, Functor1Id(Test2Functor)))
    // console.log('fa - ', fa2)
    // console.log()
    // console.log(
    //   'F.map(fa, x => f(g(x))) - ',
    //   pipe(fa2, Functor1Composition1(Test2Functor)(a_b_function)(b_c_function))
    // )
    // console.log(
    //   'F.map(F.map(fa, g), f) - ',
    //   pipe(fa2, Functor1Composition2(Test2Functor)(a_b_function)(b_c_function))
    // )

    // console.log('---------')
    // console.log()
    // console.log('Test2Functor')
    // console.log()
    // console.log('A => B => C')
    // console.log()
    // console.log('F.map(fa, x => x) - ', pipe(fa3, Functor1Id(Test3Functor)))
    // console.log('fa - ', fa3)
    // console.log()
    // console.log(
    //   'F.map(fa, x => f(g(x))) - ',
    //   pipe(fa3, Functor1Composition1(Test3Functor)(a_b_function)(b_c_function))
    // )
    // console.log(
    //   'F.map(F.map(fa, g), f) - ',
    //   pipe(fa3, Functor1Composition2(Test3Functor)(a_b_function)(b_c_function))
    // )

    // console.log('---------')
    // console.log()

    // test
    console.log('map - ', pipe(fa, F.map(Test3Functor, Test2Functor)(a_b_function)))

    console.log('flap - Test2Functor - ', F.flap(Test2Functor)(a123)(fa2_function_object))
    console.log('flap - Test3Functor - ', F.flap(Test3Functor)(a123)(fa3_function_object))

    console.log('bindTo - Test2Functor - ', pipe(fa2_123, F.bindTo(Test2Functor)('Test BindTo')))
    console.log('bindTo - Test3Functor - ', pipe(fa3, F.bindTo(Test3Functor)('Test BindTo')))

    console.log(
      'let - Test2Functor - ',
      pipe(fa2_123, F.let(Test2Functor)('Test Let', a_b_function))
    )
    console.log('let - Test3Functor - ', pipe(fa3, F.let(Test3Functor)('Test Let', a_b_function)))
  })
})

実行結果

map -  { Test3: [ { Test2: '123' }, { Test2: '456' }, { Test2: '789' } ] }
flap - Test2Functor -  { Test2: '123' }
flap - Test3Functor -  { Test3: [ '123', '246', '369' ] }
bindTo - Test2Functor -  { Test2: { 'Test BindTo': 123 } }
bindTo - Test3Functor -  {
  Test3: [
    { 'Test BindTo': 123 },
    { 'Test BindTo': 456 },
    { 'Test BindTo': 789 }
  ]
}
let - Test2Functor -  { Test2: { 'Test Let': '123' } }
let - Test3Functor -  {
  Test3: [
    { 'Test Let': '123' },
    { 'Test Let': '456' },
    { 'Test Let': '789' }
  ]
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1