class Vertex(val data : V) class Edge(val from : V, val data : E, val to : V) class Graph { private val mutableEdges = ArrayList>() // type is ArrayList, but I want IMutableList /* options: private val edges : IMutableList> = ArrayList>() private val edges : IMutableList> = ArrayList() // not an erasure, but a request to infer parameters */ private val mutableVertices = HashSet>() val edges : IList> = mutableEdges; val vertices : ISet> = mutableVertices; fun addEdge(from : V, data : E, to : V) { mutableEdges.add(Edge(from, data, to)) // constructor parameters are inferred } fun addVertex(v : V) { mutableEdges.add(Edge(from, data, to)) // constructor parameters are inferred } fun neighbours(v : Vertex) = edges.filter{it.from == v}.map{it.to} // type is IIterable> fun dfs(handler : (V) -> Unit) { val visited = HashSet>() vertices.foreach{dfs(it, visited, handler)} fun dfs(current : Vertex, visited : ISet>, handler : (V) -> Unit) { if (!visited.add(current)) return handler(current) neighbours(current).foreach{dfs(it, visited, handler)} } } public fun traverse(pending : IPushPop>, visited : ISet>, handler : (V) -> Unit) { vertices.foreach { if (!visited.add(it)) continue pending.push(it) while (!pending.isEmpty) { val current = pending.pop() handler(current); neighbours(current).foreach { n -> if (visited.add(n)) { pending.push(n) } } /* alternative pending->push(neighbours(current).filter{n => !visited[n])}) // -> means that if push(x : T) and actual parameter y is IIterable, this compiles into y.foreach{ n => push(n) } */ } } } }