點燈坊

失くすものさえない今が強くなるチャンスよ

使用 readStorage() 讀取 Local Storage

Sam Xiao's Avatar 2020-08-26

Local Storage 雖可透過 Web API 的 localStorage.getItem() 讀出,但其並非 Free Function,且若 Key 不存在會回傳 undefined,這些都不適合 Function Pipeline,Wink-fp 特別包成適合 FP 的 readStorage()

Version

macOS Catalina 10.15.6
Wink-fp 1.23.0

Functional

read000

按下 Read Local Storage 會讀出 local storage。

<template>
  <div>
    <button @click="onRead">Read Local Storage</button>
    {{ result }}
  </div>
</template>

<script>
import { bind, ifElse, isNil, pipe, always } from 'ramda'
import { create, env } from 'sanctuary'

let { Just, Nothing, fromMaybe } = create ({ checkTypes: false, env })

let readStorage = pipe(
  bind(localStorage.getItem, localStorage),
  ifElse(isNil, always(Nothing), Just)
)

let onRead = function() {
  this.result = pipe(
    always('name'),
    readStorage,
    fromMaybe('')
  )()
}

export default {
  name: 'App',
  data: () => ({
    name: 'Sam',
    result: ''
  }),
  methods: {
    onRead,
  }
}
</script>

14 行

let readStorage = pipe(
  bind(localStorage.getItem, localStorage),
  ifElse(isNil, always(Nothing), Just)
)

若使用 Web API,讀出 local storage 要使用 localStorage.getItem(),但這種 API 並非 free function,且若 key 不存在會回傳 undefined, 這些都不適合 Function Pipeline,因此重新建立 free function 且回傳 Maybe Monad 的 readStorage()

  • 使用 bind()localStorae.getItem() 取出 free function
  • 使用 ifElse() 處理 undefined,若為 undefined 則回傳 Nothing,否則回傳 Just

19 行

let onRead = function() {
  this.result = pipe(
    always('name'),
    readStorage,
    fromMaybe('')
  )()
}

因為 readStorage() 回傳為 Maybe Monad,因此要使用 fromMaybe() 從 Maybe Monad 內取出,並提供 Nothing 的預設值。

Wink-fp

<template>
  <div>
    <button @click="onRead">Read Local Storage</button>
    {{ result }}
  </div>
</template>

<script>
import { pipe, always } from 'ramda'
import { create, env } from 'sanctuary'
import { readStorage } from 'wink-fp'

let { fromMaybe } = create ({ checkTypes: false, env })

let onRead = function() {
  this.result = pipe(
    always('name'),
    readStorage,
    fromMaybe('')
  )()
}

export default {
  name: 'App',
  data: () => ({
    name: 'Sam',
    result: ''
  }),
  methods: {
    onRead,
  }
}
</script>

11 行

import { readStorage } from 'wink-fp'

let onRead = function() {
  this.result = pipe(
    always('name'),
    readStorage,
    fromMaybe('')
  )()
}

readStorage() 實務上經常使用,Wink-fp 已經內建。

readStorage()
String -> Maybe String
localStorage.getItem() 的 free function 與 Maybe 版本

String:讀取 local storage 的 key

Maybe String:回傳 Maybe Monad,讀出的 String 包在 Maybe 內

Vue 3

<template>
  <button @click="onRead">Read Local Storage</button>
  {{ result }}
</template>

<script>
import { ref } from 'vue'
import { pipe, always } from 'ramda'
import { create, env } from 'sanctuary'
import { effect } from 'vue3-fp'
import { readStorage } from 'wink-fp'

let { fromMaybe } = create ({ checkTypes: false, env })

let name = ref('Sam')
let result = ref('')

let onRead = pipe(
  always('name'),
  readStorage,
  fromMaybe(''),
  effect(result),
)

let setup = pipe(
  always({ name, result, onRead })
)

export default {
  name:'App',
  setup
}
</script>

18 行

let onRead = pipe(
  always('name'),
  readStorage,
  fromMaybe(''),
  effect(result),
)

readStorage() 要在 Vue 3 才能發揮威力。

由於 readStorage() 為 free function,因此可輕易整合進 Function Pipeline 中:

  • 使用 always() K combinator 回傳 name
  • 使用 readStorage() 讀出 local storage
  • 使用 fromMaybe() 從 Maybe Monad 取出 String
  • 使用 effect() 寫入 result reactive model

若使用 Web API 的 localStorae.getItem(),則根本無法透過 Function Pipeline 組合出 onRead()

Conclusion

  • Web API 當初是以 OOP 設計,因此與 FP 格格不入,可自己將 Web API 包成 free function 方便 Function Pipeline
  • Web API 不少都會回傳 undefined,建議這類 function 都應該包成 Maybe Monad 後回傳,除了可避免忘記處理 undefined 造成 runtime error,也方便在 Function Pipeline 處理
  • Free function 與 Maybe 在 Vue 2 看不出威力,但在 Vue 3 因為有了 Composition API,能輕易在 Function Pipeline 內使用 free function 與 Maybe Monad

Reference

MDN, Storage.getItem()