GoでMonad っぽく
GoでもMonadライブラリ書いている人もいるけど、言語のサポートがないので実用で使いたいものは見つからない。
しかし、実用で使えるエッセンスは取り入れられるはず、、ということでMonadっぽいデザインパターンを考えてみた。
厳密にはモナドではないけど、役に立つはず、、
Maybeモナド
計算の途中結果がnilになるかもしれない場合の処理を綺麗に書く。
https://wiki.haskell.org/All_About_Monads#Do_notation の羊さんの例
ポイント
Nil相当のstructを用意して、nilの場合は代わりにそれを返す
package main
import (
"fmt"
)
type Sheep struct {
Name string
Father *Sheep
Mother *Sheep
}
var NilSheep *Sheep = &Sheep{Name: "Nothing"}
func MaybeSheep(s *Sheep) *Sheep {
if s == nil {
return NilSheep
}
return s
}
func Mother(s *Sheep) *Sheep {
return MaybeSheep(s.Mother)
}
func Father(s *Sheep) *Sheep {
return MaybeSheep(s.Father)
}
func MaternalGrandfather(s *Sheep) *Sheep {
m := Mother(s)
return Father(m)
}
func MothersPaternalGrandfather(s *Sheep) *Sheep {
m := Mother(s)
f := Father(m)
return Father(f)
}
func main() {
bob := &Sheep{
Name: "bob",
Father: NilSheep,
Mother: NilSheep,
}
alice := &Sheep{
Name: "alice",
Father: bob,
Mother: NilSheep,
}
charie := &Sheep{
Name: "charie",
Father: NilSheep,
Mother: alice,
}
fmt.Println(MaternalGrandfather(charie).Name)
fmt.Println(MothersPaternalGrandfather(charie).Name)
}
Nil相当のstructがimmutableでないのが気に入らないが、
関数で返してあげても良いかも
func NilSheep() *Sheep {
return &Sheep{Name: "Nothing"}
}
Errorモナド
ContextでErrorの値を引き回す
エラーなら処理をしない
package main
import (
"fmt"
)
type Context struct {
Err error
}
func doTaskA(context *Context) {
if context.Err != nil {
return
}
fmt.Println("Task A")
}
func doTaskB(context *Context) {
if context.Err != nil {
return
}
context.Err = fmt.Errorf("failed task B")
}
func doTaskC(context *Context) {
if context.Err != nil {
return
}
fmt.Println("Task C")
}
func main() {
context := &Context{}
doTaskA(context)
doTaskB(context)
doTaskC(context)
}
その他のモナド
IOモナドとかStateモナドは、Goだとシンプルにかけるし困ってないのでいらないかな、
参照透過性を保つメリットはあるかもしれないけどオーバーヘッドが大きすぎて実用にならなそう。
何か他にGoで便利なやつあるでしょうか、
こうしてみると、GoはMonadいらないかもしれないですね、
個人的には、関数ではnilを返さないようにすると全体が綺麗に書けるというのが発見でした。
(当たり前か、、)