Skip to content

Instantly share code, notes, and snippets.

@SergProduction
Created March 1, 2020 12:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SergProduction/8d771271cbe8ddcc860117b66cbf60f0 to your computer and use it in GitHub Desktop.
Save SergProduction/8d771271cbe8ddcc860117b66cbf60f0 to your computer and use it in GitHub Desktop.

Стейт

Функциоанльный стейт это - функция которая возвращает пару, где первый элемент активный, а второй представляет хранилище. По дефолту второй элемент такого же типа как и первый, это значит если оба параметра пары будут Int, то в хранилище мы сможем хранить только один элемент. Позже мы расмотрим вариант где второй параметр будет списком, а пока давайте разберемся с концепцией функциоанльного стейта.

И так - как же нам записать в функцию что-то? как функция может хранить значение?

type State<A> = () => A

state: State<number> = () => 1

state() // 1

Вот простой пример как функция хранит внутри себя константу, можно думать о функции как о контейнере который хранить значение, а для того чтоб его достать из контейнера нужно ее вызвать

Чтобы мы могли вставить любое значение, нам понадобится сделать функцию с одним параметром, которая создает наш контейнер State.

Такая функция будет называтся pure

type PureS<A> = (a: A) => State<A>

pure = a => () => a

const s0 = pure(1)
const s1 = pure(s0() + 1)
s1() // 2
const s2 = pure(s1() * 2)
s2() // 4

// теперь запишем без констант
pure(
  pure(
    pure(
      1
    )() + 1 // 2
  )() * 2 // 4
)() // 4

// теперь развернем pure, подставим вместо него, его содержимое (это не обязательно понимать, это просто для любопытсва)
(_ => 
  (_ =>
    (_ => 1)() + 1
  )() * 2 // 4
)() // 4

инстанс функтора для стейта

Теперь чтоб не работать в ручную с нашей оберткой,
чтоб не вызывать ее каждый раз для того чтоб достать значение,
чтоб не вызывать каждый раз pure чтоб обратно завернуть значение в обертку,
сделаем функцию хелпер
который будет принимать

  • f: A => B - функцию которая будет получать значние из State, и возвращать другое значение
  • ma: State<A> - нашу обертку State из которого нужно получить значение,
  • mb: State<B> - в результате будет распоковывать ma,
    передавать это значение в нашу функцию f,
    получение значение от функции заворачивать в новый State mb
type fmap<A, B> = (f: (a:A) => B, ma: State<A>) => State<B>

fmap = (f, state) => pure(f(state()))

s = fmap(
  x => x * 2,
  fmap(
    x => x + 1,
    pure(1)
  )
)
s() // 4

Мы только что реализовали интерфейс функтора для контейнера State, интерфейс функтора включает в себя фунцию fmap

интерфейс функтора в хаскеле
class Functor m where
  fmap :: (a -> b) -> m a -> m b

про то как читать типы написанные на хаскеле см тут

именнования переменных

именновать переменные я буду как в хаскеле, посмотрите на тип fmap, там есть ma, в хаскеле m называют любую обертку которая может хранить в себе какое-то значение,
ma - значение a в обертке m.
переменные, я буду именовать в алфавитном порядке - a, b, c, ..., и дженерики для них соответсвующи

Такая запись на жс не очень красивая, есть вложенности, надо соблюдать отсупы чтоб это было более менне читаемо, и надо читать с нутри наружу, Это все потому-что я показываю имплементацию стейта, функтора ровно такими как они описанные в хаскеле. но там это не вызывает таких проблем с читаемостью, так как там есть операторы

пример на хаскеле

fmap = (<$>)
оператор <$> правоассоциативный, это значит что выполнение будет справо на лево.

(\x -> x * 2) <$> (\x -> x + 1) <$> pure 1

эквиваленто

fmap
  (\x -> x * 2)
  (
    fmap
    (\x -> x + 1)
    pure 1
  )
более читаемый способ записи для js

мы можем воспользоваться объектами жсса, чтоб чейнить методы через точку, путем возвращения самого себя каждый раз,
получится алтернативная замена операторам хаскеля

class State {
  constructor(a) { this.state = () => a }
  static pure(a) { return new State(a) }
  runState() { return this.state() }
  fmap(f) {
    return State.pure(
      f(this.state())
    )
  }
}

console.log(
  State.pure(1).fmap(x => x+1).fmap(x => x*2).runState()
)

продолжение следует

инстанс монады для стейта

...

второй параметр стейта

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment