makotan _at_ gmail dot com

プログラムを書いてどんな感じか試してみた

仕様に特に悩まないものでかつ色んな機能を使えるもの=状態管理!
そういえばScalaの練習も状態管理だったなぁ〜
ということで、書いてみた(感想は後ほど)


実際には全体を3回ほど見直して記述を試してみた結果の最後の形(色々試すのに飽きたの意味)

sealed  class Node<T>(val name: String) {
    class ActionNode<T>(name: String , val action: (T) -> Unit) : Node<T>(name)

    class StateNode<T>(name: String) : Node<T>(name)
    
}


data class NodeLink<T>(val fromNode: Node<T> , val toNode: Node<T> , val check : (T?) -> Boolean)

data class Flow<T>(val getState : (T) -> String , val setState : (T,String) -> T , val nodes : Map<String,Node<T>> , val links : Map<String,Map<String,NodeLink<T>>> )

fun <T> Flow<T>.plus(node : Node<T>) : Flow<T> {
    return this.copy(nodes = this.nodes +  Pair(node.name , node))
}

fun <T> Flow<T>.link(from: String, toName: String, check: (T?) -> Boolean) : Flow<T>?  {
    val fromNode = nodes.get(from) ?: return null
    val toNode = nodes.get(toName) ?: return null
    return link(fromNode, toNode, check);
}

fun <T> Flow<T>.link(fromNode: Node<T>, toNode: Node<T>, check: (T?) -> Boolean) : Flow<T>  {
    val nodeLink = NodeLink<T>(fromNode,toNode,check)
    val child  = links.get(fromNode.name) ?: mutableMapOf<String,NodeLink<T>>()
    val newChild  = child + Pair(toNode.name , nodeLink)
    val newLinks = this.links + Pair(fromNode.name , newChild)
    return this.copy(links = newLinks)
}


class ToNextStatusException(message: String?) : Exception(message)

class FlowExecutor<T> (val flow: Flow<T>) {
    init {
        // 本当はflowのチェック入れたかった
    }

    fun toNextStatus(data : T) : T  {
        val current = flow.getState(data)
        val currentNode = flow.nodes.get(current) ?: throw ToNextStatusException("status name not found")
        return walkNode(data, currentNode);
    }

    protected  fun checkNode(data: T , toNode : Node<T>) :T =
        when (toNode) {
            is Node.ActionNode<T> -> {
                toNode.action(data)
                walkNode(data,toNode)
            }
            is Node.StateNode<T> -> flow.setState(data , toNode.name)
        }
    

    protected fun walkNode(data: T, currentNode : Node<T>) : T {
        val nexts = flow.links.get(currentNode.name) ?: throw ToNextStatusException("status name not found")

        val nextNodes = nexts.values.filter { it.check(data) }
        return when (nextNodes.size) {
            0 -> throw ToNextStatusException("zero match")
            1 -> checkNode(data , nextNodes.first().toNode)
            else -> throw ToNextStatusException("many match")
        }
    }
    
}

判ったこと

  • IntelliJのエラーチェックの感じはJavaとなんら違わない
  • IntelliJの補完含めてJavaと同じ感じ
  • Null Safetyが言語に組み込まれているのは便利
  • Javaを今風の言語仕様に仕立て上げるとこんなになるんだ〜
  • Javaがこうだったら良いのになぁ〜がほぼ実現されてた
  • 最初の実装完了まで3時間くらいだった(Scalaの時はもっと時間かかったけど初関数型だったからかもしれない)
  • 個人的にはJavaを使うところでは積極的に推していきたい
  • 関数内ではreturn しないと駄目だった。Lambda内だと最後の評価結果が戻るのに