$ purs --version
0.12.5
前置き
"何もしない"コードを書いてみます。
module Main where
import Prelude (Unit, pure, unit)
import Effect (Effect)
main :: Effect Unit
main = pure unit -- あるいはmempty
実行結果
何も起こりません。
JavaScriptのconsole.log
をPureScriptで実装する
Effect
のJavaScript語への翻訳は案外単純で、PureScriptのEffect.Console.log
はJavaScriptのconsole.log
を無名関数でラップしたものに相当します。
log :: String -> Effect Unit
log = -- 実装 (以下はJavaScript)
-- const log = str => () => console.log(str)
PureScriptのEffect.Unsafe.unsafePerformEffect
を使うとラップした関数を無理矢理実行できます。(逆説的に実行者が隠匿されていることになります。)
unsafePerformEffect :: forall a. Effect a -> a
unsafePerformEffect = -- 実装 (以下はJavaScript)
-- const unsafePerformEffect = effectAsFunction => effectAsFunction()
したがって、console.log
に相当する処理を一応実現できます。
「unsafePerformEffect
after log
」。
PureScriptでは<<<
という中置演算子でcomposeできます。
unsafeLog :: String -> Unit
unsafeLog = compose unsafePerformEffect log
-- = unsafePerformEffect <<< log
脱法Hello, world!
module Main where
import Prelude (Unit, pure, unit, (<<<))
import Effect (Effect)
import Effect.Console (log)
import Effect.Unsafe (unsafePerformEffect)
unsafeLog :: String -> Unit
unsafeLog = unsafePerformEffect <<< log
sayHello :: Unit
sayHello = unsafeLog "Hello, world!"
main :: Effect Unit
main = pure unit
実行結果
Hello, world!
main
で何もしていないはずなのになぜか出力される気味の悪いコードが完成です。
脱法すると予測不可能な挙動を示す
module Main where
import Prelude (Unit, pure, unit, ($))
import Effect (Effect)
import Effect.Console (log, logShow)
import Effect.Random (random)
import Effect.Unsafe (unsafePerformEffect)
sayHello :: Unit
sayHello = unsafePerformEffect $ log "Hello, world!"
sayLuckyNumber :: Unit
sayLuckyNumber = unsafePerformEffect $ logShow r
sayHello2 :: Unit
sayHello2 = unsafePerformEffect $ log "Hello, world! 2"
r :: Number
r = unsafePerformEffect random
main :: Effect Unit
main = pure unit
実行結果例
Hello, world! 2
Hello, world!
0.8295374173787979
コードから実行順序を予測できません。
というのも、処理の"流れ"を決めるのはMonadの仕事と言えます。
main
はエントリーポイント
コードは定義の集まり。main
がデフォルトのエントリーポイントで、最後にunsafePerformEffect main
が1度だけ実行されることになっています。
したがって、学習目的以外でunsafePerformEffect
を使う必要はなさそうです。
module Main where
import Prelude (Unit, bind, discard)
import Effect (Effect)
import Effect.Console (log, logShow)
import Effect.Random (random)
-- main :: forall a. Effect a
main :: Effect Unit
main = do
log "Hello, world!"
r <- random
logShow r
log "Hello, world! 2"
-- 暗黙のunsafePerformEffect main
先ほどとは異なり、コードが実行順序を主張しています。
(bind
に書き直すと明確。)
Effect
がMonadなので……
複数のEffect
が担う処理をmain
一つにまとめることができます。
まとめかたが処理の"流れ"を決めます。
- Applicativeスタイルは並列処理的。
- ただし、普通に実装してもJavaScriptの都合でコンカレント。
- WebWorkerを使うライブラリかFFIでパラレルも実現可能……?
- Monadスタイル(do記法)は逐次処理。
- Monadは表現力が高い。Applicativeは抽象度が高い。