LoginSignup
2
2

More than 5 years have passed since last update.

GoでMonad っぽく

Last updated at Posted at 2017-03-24

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を返さないようにすると全体が綺麗に書けるというのが発見でした。
(当たり前か、、)

2
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
2
2