Scala関数型デザイン&プログラミングを読み直した

  • 3
    いいね
  • 0
    コメント

Scalaを使わない1年が過ぎた。しかしFPとKotlinとSwiftを鍛えた今ならFPinScalaの資産を使い回せるはずだ。
(前にもScala→Swiftしようとしたけど不足しているものが多すぎてちからつきた。)

FPinScalaのコードは省略。Scalaの記事だけど著作権怖いから手元でみてください。
代表して10章モノイドをトライ。

ここ最近の私の周辺のScala事情

大学生です。

  • コップ本の存在は(一部には)知られているらしい。生協にも置いてある。FPinScalaはわからない。
  • PHPerサークルの後輩達の何人かがScalaに浮気したり異動したりしているらしい。Javaライクな書き方に留まっているのか深淵を覗く勢いなのかまでは聞いてない。
  • 大学のグループ課題でわざわざScalaを選ぶ奴はいない。共通語はJava、あとは学習コストが少なくて済むRailsに飛びつく。よしみんなで1からScalaやろうぜとはならない。ダメ絶対。

すっかりFPを勉強するためだけのScalaになってしまった。Haskell書かないのにすごいHaskell読んでる感じ。

以下本題。

第10章モノイド

trait Monoid[A]

  • Kotlin
    • trait は廃止
  • Swift
    • protocol Monoid<A> という書き方はできない。
    • なぜ static かは後述
interface Monoid<A> {
    fun op(a1: A, a2: A): A
    val zero: A
}
protocol Monoid {
    associatedtype A
    static func op(a1: A, a2: A) -> A
    static var zero: A { get }
}

Monoidインスタンス

  • Kotlin
  • Swift
    • struct にするのが正解なのかは謎。でも呼ぶにはそうせざるを得ない気がする。
    • 相変わらず foldLeftreduce と呼ぶ謎。 foldRight は存在しないはず。
val stringMonoid = object: Monoid<String> {
    override fun op(a1: String, a2: String): String = a1 + a2
    override val zero = ""
}

class ListMonoid<A>: Monoid<List<A>> {
    override fun op(a1: List<A>, a2: List<A>) = a1.plus(a2)
    override val zero = listOf<A>()
}    
struct StringMonoid: Monoid {
    typealias A = String

    static func op(a1: String, a2: String) -> String {
        return a1 + a2
    }

    static var zero: String {
        return ""
    }
}

struct ArrayMonoid<T>: Monoid {

    typealias A = [T]

    static func op(a1: [T], a2: [T]) -> [T] {
        return a1 + a2
    }

    static var zero: Array<T> {
        return []
    }
}

実際にはこんなことしないで

let s = words.fold("") { a, b -> a + b }

とか

let s = words.reduce("", +)

とか書くんだろうけど。
珍しくSwiftが1番短い。でもreduceかっこ悪い。

sealed trait WC

  • Kotlin
    • data classは他から継承できないので、Scala版のようにはいかない。まあ何とかなるかな?
sealed class WC {
    class Stub(val chars: String): WC()
    class Part(val lStub: String, val words: Int, val rStub: String): WC()
}
enum WC {
    case stub(chars: String)
    case part(lStub: String, words: Int, rStub: String)
}

調べてる過程でこういうの見つけた。
Scala vs Kotlin – Agilewombat

mapMergeMonoid

  • Kotlin
    • class MapMergeMonoid<K, V>(val monoidV: Monoid<V>): Monoid<Map<K, V>> などと書き出せばいけないこともないけど、結局staticメソッドを持ってこれない問題が残る。
  • Swift
    • Scala/KotlinみたいにProtocolが総称型を扱えないので class AnyMonoid<V>: Monoid をかませないと大変なことになりそう。

こいつはしんどい。Scalaのかち。

まとめ

最近はいろんな言語が関数型を扱えるようになって、いろいろ遊ぶのに必要な関数 ( map とか) も増えてきた。しかし型の表現ではやはり制約がついて、Scalaみたいに直感的に書くほどには至らず、概念を各言語に落とし込むには手間がかかる。やっぱりクラスやインターフェースに落とさずに直接操作するのが無難そう。

Scala先輩はやっぱりすごい!

この投稿は Scala Advent Calendar 201623日目の記事です。