TypeScriptで作成した State Monadをモジュールにしてみた。
importの指定に、若干迷いましたので共有します。
今回の目標
TypeScriptのモジュール化を行う。
モジュールの配置
State Monadモジュールは、ファイル名stateMonad.tsとし、root/monad/state/stateMonad.tsに配置する。
Stackモジュールは、ファイル名stack.tsとし、root/stack/stack.tsに配置する。
テストモジュールは、ファイル名stackTest.tsとし、root/stackTest.tsに配置する。
State Monadモジュール
export
module monad.state {
export
interface Tuple2<A,B> {
fst: A
snd: B
}
export
class State<T,X>{
runState: (t:T) => Tuple2<X,T>
evalState = (t:T):X => this.runState(t).fst
execState = (t:T):T => this.runState(t).snd
constructor(g:(t:T) => Tuple2<X,T>){
this.runState = g
}
static returns<T,X>(v:X):State<T,X>{
return new State<T,X>((t:T) => { return {fst:v, snd:t } })
}
bind<B>(f: (x:X) => State<T,B>):State<T,B>{
var g = (t:T) => {
var st = this.runState(t)
return f(st.fst).runState(st.snd)
}
return new State<T,B>(g)
}
}
export
class MonadState {
static get<T>():State<T,T> {
var g = (t:T) => {
return { fst: t, snd: t }
}
return new State<T,T>(g)
}
static put<T>(a:T):State<T,void> {
var g = (t:T) => {
return { fst: null, snd: a }
}
return new State<T,void>(g)
}
}
}
モジュールとして外部に提供するmodule、classにはexportを付加した。
Tuple2は本来なら別モジュールとするべきだが、今回はこのモジュールに含めた。
Stackモジュール
import ms = require("../monad/state/stateMonad")
export
module stack {
export
class Stack<X> {
pop = <X>():ms.monad.state.State<X,X[]> =>
ms.monad.state.MonadState.get<X[]>()
.bind((e:X[]) => {
return ms.monad.state.MonadState.put<X[]>(e.slice(1))
.bind(k => ms.monad.state.State.returns(e[0])) })
push = <X>(i:X):ms.monad.state.State<X,void> =>
ms.monad.state.MonadState.get<X[]>()
.bind((e:X[]) =>
ms.monad.state.MonadState.put<X[]>([i].concat(e)))
}
}
外部モジュールのstateMonad.tsを使用するため、importが必要。
requireはこのモジュールからの相対パスを指定する。
また、外部モジュールを使用する際は、importに使用したms.にフォルダ名monad.state.を付加して使用する。
テストモジュール
import ms = require("./monad/state/stateMonad")
import st = require("./stack/stack")
var fnc = <X>(st:st.stack.Stack):ms.monad.state.State<X,X[]> =>
st.pop()
.bind(e => st.pop())
.bind(e => st.push(20))
.bind(e => st.pop())
.bind(e => st.push(e*5))
console.log(fnc(new st.stack.Stack<number>()).execState([1,2,3])) //=> [100,3]
外部モジュールのstateMonad.ts、stack.tsのimportが必要。
ここでも相対パスを指定する。
コンパイル
コンパイルする際は、--module commonjsのオプションを付加する。
Webで見ると--module amdでも良いと書いてあるが、私の環境では実行時にエラーとなった。
rootに移動し、コンパイルコマンドを発行する。
ts stateTest.ts --module commonjs
実行
rootに移動し、node.jsにて実行する。
node stateTest.js // [100,3]
期待した値が表示された。
まとめ
以前importに指定していたmoduleがdeprecatedになっているため、requireを使用する。
requireには相対パスを指定する(別の方法があるかもしれません)。
外部クラスを使用する場合は、importに使用した変数(今回の場合はms)と、フォルダ階層を.で区切り指定する。
コンパイル時には、--module commonjsのオプションを付加する。