来世から頑張る!!

技術ブログを目指して

型安全なリストを作りたい Part. 1

最終的な目標としては、行列の計算がしたい。

  1 2 3       1 4       1*1+2*2+3*3 1*4+2*5+3*6       14 32
(       ) * ( 2 5 ) = (                         ) = (       )
  4 5 6       3 6       4*1+5*2+6*3 4*4+5*5+6*6       32 77

こんな感じのやつ。
2行3列の行列は3行2列の行列との積を求めることができて、結果は2行2列の行列になる。

もう少し一般化すると、m行n列 * n行p列 = m行p列で、 1つ目の行列の列数と2つ目の行列の行数が一致していないといけない。

これを普通にコンパイラが判定してくれるようにできればいいなという話。

ただ、全通りつくるとかはどう考えても現実的ではないので、 サイズ付きの行列を作ろうというのが主題です。

ちなみに、shapelessというライブラリにはSizedListみたいな型があるそうなので、
仕事とかですぐに必要であればそちらのソースを読んだりするべきですが、
今回は思考そのものを楽しむことが目的なので特に原典もなく間違いも気にせず進みます。

1. サイズを型で表現する。

いきなり難しいことは考えずに、とりあえずサイズ付きの集合型を考えることから始めます。
EmptyList, OneItemList, TwoItemsListのようなのがList[0], List[1]ってできればいいのですね。

まずは単純に数値0を表す型を作ってみます。

sealed trait Size
sealed trait Zero extends Size

ここまでは決め事なのでこれでいいとして、問題はこの先です。 One, Twoなんて続けていったらいつまで経っても終わりません。

そこで、ちょっと前に書いたScalaの型推論って難しいのチャーチ数を使ってみます。

final case class Succ[S <: Size]() extends Size

これで1がSucc[Zero], 2がSucc[Succ[Zero]]なんてふうに表現できるようになりませんか?
どんどん長くなっていくけどこの際気にしない。

2. サイズを型引数にもつリストを定義

これが正しく動作するのかを試すために、Listを定義してみます。
名前は標準ライブラリとかぶらないように適当につけます。

trait SizedList[S <: Size, +A] {
  def :+:[B >: A](b: B): SizedList[Succ[S], B] = new :+:(b, this)
}
case object SizedNil extends SizedList[Zero, Nothing]
case class :+:[S <: Size, A](head: A, tail: SizedList[S, A]) extends SizedList[Succ[S], A]

object SizedList {
  def empty[A]: SizedList[Zero, A] = SizedNil
}

replで動作確認。。。

scala> val a = SizedList.empty[Int]
a: SizedList[Zero,Int] = SizedNil

scala> val b = 2 :+: SizedList.empty
b: SizedList[Succ[Zero],Int] = :+:(2,SizedNil)

scala> val c = 9 :+: 1 :+: 2 :+: 4 :+: 7 :+: SizedList.empty
c: SizedList[Succ[Succ[Succ[Succ[Succ[Zero]]]]],Int] = :+:(9,:+:(1,:+:(2,:+:(4,:+:(7,SizedNil)))))

とりあえず問題なく動いてはいそうです。
まだ値を取り出すことすらできませんが。

次回予告(いつ!?)

次回はこれにheadtailを足してみましょう。

まだ漠然としか考えていませんが、値がOrderedじゃないとソートできないとかそういうのと同じ機構を使えばできそうです。

reverseが定義できればきっとそれなりのことができるはずです。

Scala勉強会で発表してきた。

初スライド!!!

for の使い方について。 http://kazzna.jp/slide/scala_for/index.html

Haskellモナド系ブログが大体doを理解するならStateだってなってたので便乗です。

勉強会でも口頭で言ったけれど、 flatMapの定義をするなら Monad を覚えないと危険です!!

あと、カッコの書き方について教わったのであとであとでスライドをなおす予定。

次回のスライドを作成中。

Scalaの型推論って難しい

ラムダ計算とかの例によくある チャーチ数Scalaで試してみる。

原点を表す Zero 、次を表す Succ を定義しておけば足し算とかできるってやつ。

まずは試し書き

import Conrtol.Applicative

zero f a = a
succ n f a = f $ n f a

one = succ zero
two = succ one

add a b = liftA2 (.) a b

toInt n = n (\a -> a + 1) 0
toAs n = n (\a -> 'a' : a) []

a = add (succ two) one

main = do
    putStrLn $ show $ toInt a // => 4
    pusStrLn $ show $ toAs a // => "aaaa"

わざと型は明記していない。haskellなどのきちんと型推論してくれる言語であればこれで動く。

もしくは動的型の言語でも。

def zero(f):
    def x(a):
        return a
    return x

def succ(n):
    def x(f):
        def y(a):
            return f(n(f)(a))
        return y
    return x

one = succ(zero)
two = succ(one)

def add(n):
    def x(m):
        def y(f):
            def z(a):
                return n(f)(m(f)(a))
            return z
        return y
    return x

def to_int(n):
    return n(lambda x: x + 1)(0)

def to_as(n):
    return n(lambda x: "a" + x)("")

a = add(succ(two))(one)

to_int(a) # => 4
to_as(a) # => 'aaaa'

Scalaの場合

これがScalaだとまあ大変で。。。

def zero[A](f: A => A)(a: A) = a

この時点で上記の例ではなんでもよかった(使ってない) f の型を固定。
固定しない場合型指定が大変になる(後述)。

なお、戻り値の型は書くべきだと思うけれども、今回は型推論具合を知りたいから書かなくても動作する限り省略。

def succ[A](n: (A => A) => A => A)(f: A => A)(a: A) = f(n(f)(a))

def one[A]: (A => A) => A => A = succ(zero)
def two[A]: (A => A) => A => A = succ(one)

import scalaz._
import Scalaz._  // Applicativeの導入(必須ではないけど・・・)
def add[A](x: (A => A) => A => A)(y: (A => A) => A => A) = (x |@| y) { case (a, b) => a compose b }

def toInt(n: (Int => Int) => Int => Int) = n(a => a + 1)(0)
def toAs(n: (List[Char] => List[Char]) => List[Char] => List[Char]) = n(a => 'a' :: a)(List.empty)

// def a[A]: (A => A) => A => A = add(succ(two))(one) // => 型エラー!!
def a[A]= add[A](succ(two))(one)

println(toInt(a)) // => 4
println(toAs(a)) // => List(a, a, a, a)

型が必須な上に、 val が型引数を受け付けてくれないからギリギリまで def じゃないといけない。

できる限りHaskellでやった時の型に近づけると以下のような感じ?

def zero[F, A](f: F)(a: A) = a
def succ[A, B, C](n: (B => C) => A => B)(f: B => C)(a: A) = f(n(f)(a))

def one[A, B]: (A => B) => A => B = succ(zero)
def two[A]: (A => A) => A => A = succ(one)

def add[A, B, C, D](x: A => B => C)(y: A => D => B) = (x |@| y) { case (a, b) => a compose b }

def toInt(n: (Int => Int) => Int => Int) = n(a => a + 1)(0)
def toAs(n: (List[Char] => List[Char]) => List[Char] => List[Char]) = n(a => 'a' :: a)(List.empty)

// def a[A]: (A => A) => A => A = add(succ(two))(one) // => 型エラー!!
def a[A] = add[(A => A), A, A, A](succ(two))(one)

println(toInt(a)) // => 4
println(toAs(a)) // => List(a, a, a, a)

途中から A => A =>... みたいなのを書くことを考えれば、最初の方が綺麗な気がする。。。

最後に蛇足だけど引数の順序をScalaっぽくしてみる。

def zero[A, F](a: A)(f: F) = a
def succ[A, B, C](n: A => (B => C) => B)(a: A)(f: B => C) = f(n(a)(f))

def one[A, B]: A => (A => B) => B = succ(zero)
// def one[A, B] = succ[A, A, B](zero) _  // <- これでも通るのは通るみたい
def two[A]: A => (A => A) => A = succ(one)
// def two[A] = succ[A, A, A](one) _

def flip[A, B, C](f: A => B => C) = (b: B) => ((a: A) => f(a)(b)) // これどっかにないのかな???
// scalaz.Function2Optsならあるけど・・・ uncurriedみたいなのが必要だよなぁ。
def add[A, B, C, D](x: A => B => C)(y: C => B => D) = flip((flip(x) |@| flip(y)) { case (a, b) => a andThen b })

def toInt(n: Int => (Int => Int) => Int) = n(0)(a => a + 1)
def toAs(n: List[Char] => (List[Char] => List[Char]) => List[Char]) = n(List.empty[Char])(a => 'a' :: a)

// def a[A]: A => (A => A) => A = add(succ(two))(one) // => 型エラー!!
def a[A] = add[A, (A => A), A, A](succ(two))(one)

println(toInt(a)) // => 4
println(toAs(a)) // => List(a, a, a, a)

何度 A => と書いただろう・・・???

atomのMarkdown Previewのフォント設定

atomエディタのMarkdownプレビューのCSS設定がいつの間にか変わっていたのでメモ。

目的は日本語のフォントにしたい。デフォルトだと中国系だからね

検索するといくつか出てくるけど、 setting -> Open Config Folder で、 styles.less を修正する。

/*
* Your Stylesheet
*
* This stylesheet is loaded when Atom starts up and is reloaded automatically
* when it is changed.
*
* If you are unfamiliar with LESS, you can read more about it here:
* http://www.lesscss.org
*/

.tree-view {
     font-family: "Migu 1C", sans-serif;
}

// Markdown Preview
.markdown-preview {
     font-family: "Migu 1C", sans-serif;
     h1, h2, h3, h4, h5, h6 {
          font-family: "Migu 1C", sans-serif;
     }
     code, blockquote, atom-text-editor {
          font-family: "Migu 1M", "Meiryo", monospace;
     }
}

.tree-view はどこのことかわからないのでとりあえず書いただけだけど、 重要なのは .markdown-preview って方。

ここに font-family で好きなフォントを指定。 これで基本的には変わるんだけど、コード部分とかのバッククォート部分が変わらない。

なのでmarkdown-previewのスタイルを確認。

github.com

code とか blockquote とかが怪しいと思い追加してみるも変わらなかった。
結果的には atom-text-editor ってのがコード部分らしい。

まあ、とりあえず全部書いておいた。

Python入門のためtoolz入れてみた

pythonは文法が単純で覚えやすいらしい。

class MyClass:
  def __init__(self, init_value):
    self.value = init_value
  def my_method(self, value):
    return self.value + value

instance = MyClass(5)
print instance.my_method(3) # => 8
print MyClass.my_method(instance, 3) # => 8

あと、なんか特別な意味を持つメソッドは全部 __???__ みたいに _ 2つで囲まれてるらしい。

ということで入門。
細かいことを気にせずtoolzというライブラリを入れる。

・・・ために準備

pyenvのインストール

pythonはシステムに食い込んでいるらしいので、別のpythonを気軽にいじれるツールを導入。

> git checkout https://github.com/yyuu/pyenv.git ~/.pyenv
> echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
> echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
> echo 'eval "$(pyenv init -)"' >> ~/.bash_profile

if文とかは適当に。

> git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv
> echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile

ローカルpythonのインストール

好きなバージョンのpythonをインストールできる。 今回はやむにやまれぬ事情で2系

> pyenv install 2.7.9
> pyenv virtualenv 2.7.9 my-env-name-2.7.9

上がpythonのインストールで、下が依存ライブラリが入る環境のインストール。 プロジェクト毎に依存関係を分けるためのやつ。

で、あとは自分のプロジェクトフォルダを作って、バージョン指定

> mkdir /my/project/root/dir
> cd /my/project/root/dir
> pyenv local my-env-name-2.7.9

最後の指定を行うことで、このディレクトリでのpythonが自動的に指定したバージョンになる。

pythonを使ってみる。

何はともあれtoolzをインストール。

> pip install toolz
> pip freeze > freeze

これで my-env-name-2.7.9 にだけtoolzが入ったはず。 python コマンドでpythonインタープリターを起動し試してみる。

>>> import toolz
>>> toolz.map(lambda x: x + 1, [1, 3, 5, 7, 9])
<itertools.imap object at 0x7f3cfdf1af90>
>>> for a in toolz.map(lambda x: x + 1, [1, 3, 5, 7, 9]):
...   print a
...
2
4
6
8
10
>>> exit()

ひとつ上のディレクトリーに移動して、同じく実行。

>>> import toolz
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ImportError: No module named toolz
>>> exit()

いい感じ。

あと、ファイルにコードを書くときのshebangはenvを使わないといけないみたいなので注意。

#! /usr/bin/env python
# coding: utf-8

toolzを使ってみる。

toolzは難しいプログラミングを簡単にしてくれるためのツールで、swiftzみたいなものらしい。

さっき使ったmapなど、遅延実行してくれるのででっかいファイルの処理するのに使用した。

>>> infile = open('inflie.txt', 'r')
>>> lines = toolz.map(some_function, infile)
>>> outfile1 = open('outfile1.txt', 'w')
>>> outfile2 = open('outfile2.txt', 'w')
>>> for line in lines:
...   if some_condition(line):
...     outfile1.write(line)
...   else:
...     outfile2.write(line)
...
>>> outfile2.close()
>>> outfile1.close()
>>> infile.close()

まあtryとかは適時使用するとして、こんな感じでやった時に、for式のlineを取り出した段階で初めて some_function が実行される。

toolz.map で帰ってくる itertools.imap object ってやつは1回しか評価できない。やっててこれでハマった。 list(map(f, [1, 2, 3])) とかすればlist化できるけど、これだと普通の組み込みのmapでいい気がするし、どうするのがベストなんだろう?

で、ここで問題はコードに出てきた some_functionmap の第1引数は引数を1個とって戻り値も1個の関数なので、複雑な処理をしようと思うと長い複雑な関数が必要になる。

関数合成

それは困るので、toolzを使って関数の合成。

>>> a = lambda x: x + 1
>>> b = lambda x: x * 3
>>> c = lambda x: x - 2
>>> x = toolz.compose(a, b, c)
>>> x(3) # == a(b(c(3)))
4

これで複数の関数をつなげて実行できるんだけど、どうも使いづらい。 だって、aから並べたらaから実行してほしいじゃない?

これに近い関数にpipeがある。

>>> toolz.pipe(2, a, b, c) # == c(b(a(2)))
7

でもこれは可変長引数の関係からか評価する値を先に書かなくちゃダメ。 仕方がないので新しい関数を定義してみた。

def chainf(*f):
  from toolz import pipe
  return lambda x: pipe(x, *f)
>>> x = chainf(a, b, c)
>>> x(2) # == c(b(a(2)))
7

これ、元からあるなら誰か教えて。。。

複数の関数がつなげられるようになったので、次の問題に挑戦。

部分適用

map は引数1の戻り値1なので、二つの引数を受け取るような関数が使えない。

これを解決するためには、部分適用を使用する。 そうすればテストするときは関数単位でテストできるし、合成もできるので便利なはず。

def with_contains(values, target):
  if target in values:
    return (target, True)
  else:
    return (target, False)

この例だとif文いらないけど、まあもう少し複雑な前提で。

これを toolz.map で使って各値をtupleにしたいと。 まあ、lambdaとか使ってvaluesを固定してしまえばいいんだけど、toolzには便利な curry があるので使おう。

>>> w = toolz.curry(with_contains) # w(values)(target) == with_contains(values, target)
>>> x = w([1, 2, 3])
>>> x(2)
(2, True)
>>> x(4)
(4, False)

curry複数引数の関数を1引数で1戻り値(関数が帰ってくる)の関数に変えた関数を返してくれる。

>>> uc = lambda a, b, c, d: a + b + c + d
>>> cd = curry(uc) # uc(a, b, c, d) == cd(a)(b)(c)(d)

これで1引数まで持っていければ、小さなテスト済みの関数の組み合わせで最初の map が実行できる。

もちろんoutputのファイルに書き込む部分も関数化できるんだけど、テスト方法がよくわからないからループしてログに書くだけにした。

toolz.do とかを使えば処理自体は難しくないんだけど、ファイル出力のテストってどうやるのかがわからない。

テストの書き方勉強会とかないかなぁ。

ハノイの塔をScalaで解く

ハノイの塔自体は再帰を使えば簡単に解けることは有名で、Googleれば大体すぐ出てくる。

def hanoi(height: Int, from: String, via: String, to: String): Unit = {
  if (height <= 0) {
    ()
  } else {
    // 1. スタート位置にある一番下以外のものをすべて関係のない場所に動かして
    hanoi(height - 1, from, to, via)
    // 2. スタート位置にある一番下を目的地に動かして
    println(s"Move disk ${height} from ${from} to ${to}.")
    // 3. `1`で関係のないところによけておいたものを全て目的地に移動させる。
    hanoi(height - 1, via, from, to)
  }
}

> hanoi(3, "left", "middle", "right")
Move disk 1 from left to right.
Move disk 2 from left to middle.
Move disk 1 from right to middle.
Move disk 3 from left to right.
Move disk 1 from middle to left.
Move disk 2 from middle to right.
Move disk 1 from left to right.

ただ、このままだと末尾再帰でないという理由でいつかはスタックが足りなくなるはず。 それより前に計算量が大きすぎて帰ってこなくなりそうな気もするけど。。。

ということで末尾再帰にしたい!たぶん。

関数らしくするために、値を返そう。

case class HanoiStep(height: Int, from: String, via: String, to: String)
def hanoi1(height: Int, from: String, via: String, to: String) = (HanoiStep, String, HanoiStep) = {
  val pre = HanoiStep(height - 1, from, to, via)
  val move = s"Move disk ${height} from ${from} to ${to}."
  val post = HanoiStep(height - 1, via, from, to)
  (pre, move, post)
}

Tree構造!!

自分でprintすべきものがrootにあって、左に自分より前にprintすべきものの塊、右に自分より後のがある。

左の枝、自分自身、右の枝の順で走査していけば結果になるはず!!

・・・Treeの走査って再帰でどうやるのん??? わからないから後で考えるとして、一番左の要素だけ展開すれば末尾再帰できるはずだからそれをやろう。

import scala.annotation.tailrec

sealed trait HanoiStep
case class MultiSteps(height: Int, from: String, via: String, to: String) extends HanoiStep
case class SingleStep(id: Int, from: String, to: String) extends HanoiStep

def expand(ms: MultiSteps): List[HanoiStep] = ms.height match {
  case n if n <= 0 => List.empty
  case 1 => List(SingleStep(1, ms.from, ms.to))
  case _ => {
    val pre = MultiSteps(ms.height - 1, ms.from, ms.to, ms.via)
    val ss = SingleStep(ms.height, ms.from, ms.to)
    val post = MultiSteps(ms.height - 1, ms.via, ms.from, ms.to)
    List(pre, ss, post)
  }
}

@tailrec
def expandHead(ss: List[HanoiStep]): List[HanoiStep] = ss match {
  case ((h: MultiSteps) :: t) => expandHead(expand(h) ++ t)
  case _ => ss
}

> val a = MultiSteps(5, "left", "middle", "right")
a: MultiSteps = MultiSteps(5,left,middle,right)

> val b = expand(a)
b: List[HanoiStep] = List(MultiSteps(4,left,right,middle), SingleStep(5,left,right), MultiSteps(4,middle,left,right))

> val c = expandHead(b)
c: List[HanoiStep] = List(SingleStep(1,left,right), SingleStep(2,left,middle), MultiSteps(1,right,left,middle), ....

これをとりあえずStreamでくるめば、それっぽくは動きそう。

def unfold[A, B](a: A)(f: A => Option[(B, A)]): Stream[B] = f(a) match {
  case Some((b, a)) => Stream.cons(b, unfold(a)(f))
  case _ => Stream.empty
}

def hanoi(height: Int, from: String, via: String, to: String): Stream[String] = {
  import scala.annotation.tailrec
  sealed trait HanoiStep
  case class MultiSteps(height: Int, from: String, via: String, to: String) extends HanoiStep
  case class SingleStep(id: Int, from: String, to: String) extends HanoiStep
  def f1(ms: MultiSteps): List[HanoiStep] = ms.height match {
    case n if n <= 0 => List.empty
    case 1 => List(SingleStep(1, ms.from, ms.to))
    case _ => {
      val pre = MultiSteps(ms.height - 1, ms.from, ms.to, ms.via)
      val ss = SingleStep(ms.height, ms.from, ms.to)
      val post = MultiSteps(ms.height - 1, ms.via, ms.from, ms.to)
      List(pre, ss, post)
    }
  }
  @tailrec
  def f2(ss: List[HanoiStep]): List[HanoiStep] = ss match {
    case ((h: MultiSteps) :: t) => f2(f1(h) ++ t)
    case _ => ss
  }
  val init = f1(MultiSteps(height, from, via, to))
  unfold(init)(a => f2(a) match {
    case (h: SingleStep) :: t => Some((s"Move disk ${h.id} from ${h.from} to ${h.to}.", t))
    case _ => None
  })
}

> val a = hanoi(3, "left", "middle", "right")
a: Stream[String] = Stream(Move disk 1 from left to right., ?)

> a.foreach(println)
Move disk 1 from left to right.
Move disk 2 from left to middle.
Move disk 1 from right to middle.
Move disk 3 from left to right.
Move disk 1 from middle to left.
Move disk 2 from middle to right.
Move disk 1 from left to right.

> val b = hanoi(4, "left", "middle", "right").zipWithIndex
b: scala.collection.immutable.Stream[(String, Int)] = Stream((Move disk 1 from left to middle.,0), ?)

> val c = b.map { case (a, b) => s"${b + 1}: ${a}" }
c: scala.collection.immutable.Stream[String] = Stream(1: Move disk 1 from left to middle., ?)

> c.foreach(println)
1: Move disk 1 from left to middle.
2: Move disk 2 from left to right.
3: Move disk 1 from middle to right.
4: Move disk 3 from left to middle.
5: Move disk 1 from right to left.
6: Move disk 2 from right to middle.
7: Move disk 1 from left to middle.
8: Move disk 4 from left to right.
9: Move disk 1 from middle to right.
10: Move disk 2 from middle to left.
11: Move disk 1 from right to left.
12: Move disk 3 from middle to right.
13: Move disk 1 from left to middle.
14: Move disk 2 from left to right.
15: Move disk 1 from middle to right.

とりあえず動いた。

ツリーを走査する簡単なやり方とか、もしくは継続?かなんかのテクニックを使えばもっとシンプルになるんだろうなぁ。

続きはまた次回。

MonadTって自動でMonadにはならないの???

Monadって難しいって話。

元ネタは xuweiさんのブログ

いやね、最近PlayでWebアプリ作ろうと頑張ってるんですよ。
そこでログ出力のために、引数で渡すものを必要に応じてWriterにできればなとか考えてたんですよ。
結果うまくはいかなかったんですが。

本編以外を極力省くため、試すコードはただの足し算。

def add(a: Int, b: Int): Int = a + b

これをブログを参考にIdでくるむ。

import scalaz._, Scalaz._

def add(a: Id[Int], b: Id[Int]): Id[Int] = for {
  x <- a
  y <- b
} yield x + y

// 実行
> val a = add(1, 2)
a: scalaz.Scalaz.Id[Int] = 3

それぞれIdに入っちゃったから取り出すためのfor書いて、中の値を使って処理。

これをさらにブログに書いてある通り、任意の入れ物から取り出せるように変更。

import scalaz._, Scalaz._
import scala.language.higherKinds

def add[F[_]](a: F[Int], b: F[Int])(implicit F: Monad[F]): F[Int] = for {
  x <- a
  y <- b
} yield x + y

// 実行
> val a = add[Id](1, 2)
a: scalaz.Scalaz.Id[Int] = 3

[Id]って書かないと実行できなくなった。。。
まあそこはIdで使うかどうかなのできっと問題無いのかな?
OptionやListで渡していれば予想通りの動き。

> val a = add(1.some, 2.some)
a: Option[Int] = Some(3)

> val b = add(List(1, 3, 5), List(7, 8))
b: List[Int] = List(8, 9, 10, 11, 12, 13)

MonadTransformer使って合成

> val a = List(1, 2, 3).liftM[OptionT]
a: scalaz.OptionT[List,Int] = OptionT(List(Some(1), Some(2), Some(3)))

> val b = List(5, 7).liftM[OptionT]
b: scalaz.OptionT[List,Int] = OptionT(List(Some(5), Some(7)))

> val c = add(a, b)
error: no type parameters for method add: (a: F[Int], b: F[Int])(implicit F: scalaz.Monad[F])F[Int] exist
so that it can be applied to arguments (scalaz.OptionT[List,Int], scalaz.OptionT[List,Int])

!?!?
OptionT[List, Int]なんてMonadじゃねぇぞ!!!って言われる。なんで?なんで?

よーしパパMonadとか実装しちゃうぞー(結婚したい)

type OptionTList[A] = OptionT[List, A]
implicit val OptionTListMonad = new Monad[OptionTList] {
  def point[A](a: => A) = List(a).liftM[OptionT]
  def bind[A, B](fa: OptionTList[A])(f: A => OptionTList[B]) = fa flatMap f
}

> val c = add(a, b)
error: no type parameters for method add: (a: F[Int], b: F[Int])(implicit F: scalaz.Monad[F])F[Int] exist
so that it can be applied to arguments (scalaz.OptionT[List,Int], scalaz.OptionT[List,Int])

通らない・・・

> val c = add[OptionTList](a, b)
c: OptionTList[Int] = OptionT(List(Some(6), Some(8), Some(7), Some(9), Some(8), Some(10)))

これなら通る。 つまりこう?

> val a: OptionTList[Int] = List(1, 2, 3).liftM[OptionT]
a: OptionTList[Int] = OptionT(List(Some(1), Some(2), Some(3)))

> val b: OptionTList[Int] = List(5, 7).liftM[OptionT]
b: OptionTList[Int] = OptionT(List(Some(5), Some(7)))

> val c = add(a, b)
c: OptionTList[Int] = OptionT(List(Some(6), Some(8), Some(7), Some(9), Some(8), Some(10)))

むぅー。。。
やり方を間違っている気しかしない。。。

やっぱり元ブログみたいにTraitでくるんでコンストラクターを中で呼び出さないとダメなのかな?
それとも、Monadの代わりに何かそれっぽいのがMonad Transformer用にある???

2015/04/23 追記 Scala勉強会にてねこはる先生に教えてもらった。

> val a = List(2, 3, 5).liftM[OptionT]
a: scalaz.OptionT[List,Int] = OptionT(List(Some(2), Some(3), Some(5)))

> val b = List(1, 9).liftM[OptionT]
b: scalaz.OptionT[List,Int] = OptionT(List(Some(1), Some(9)))

> val c = add[({type F[A] = OptionT[List, A]})#F](a, b)
c: scalaz.OptionT[[+A]List[A],Int] = OptionT(List(Some(3), Some(11), Some(4), Some(12), Some(6), Some(14))

Scala型推論オブジェクト指向的継承のために残念な部分が多い」ってのはこういうことなのかなぁ?