LoginSignup
4
2

More than 3 years have passed since last update.

PureScriptで脱法Hello, world!

Last updated at Posted at 2019-04-21
$ 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は抽象度が高い。
4
2
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
4
2