Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

GoでMonad っぽく

More than 3 years have passed since last update.

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away