2/3/08

Playing with Scala 4: Abstract types and Self-types

Thanks to the scala mailing list, and to Eric, I have a satisfying enough model of a graph to be used by the Dijkstra's algorithm.
(well, there already was a generic graph and directed graph written in Scala... talking about reinventing the wheel!. The solution in Eric's comment works great too... is almost the same, but he was faster than me on coming up with it :D )
http://www.scala-lang.org/intro/abstracttypes.html
http://www.scala-lang.org/intro/selfrefs.html
I just used the same code, and changed the nodes and edges to use sets instead of lists (really, we don't care about the order)
The Scala example of Graph uses abstract types: it declares type Node and type Edge as type "parameters" to be defined by concrete implementation. Even more, by declaring type Node <: NodeIntf, we're restricting Node to be subtypes of NodeIntf. The declaration "self: Node =>" tells "I'll use self as Node type"
Then I declared the trait Weighted and extended the class DirectedGraph adding my extensions to NodeImpl and EdgeImpl to use weighted trait and add the node label and the previous property. I had to add a collection of the adjacent arcs, because they weren't in the original Graph and is easier for the algorithm. I overrode the connectWith method to keep track of the adjacent arcs as they're added, also I defined our --> operator to call the connectWith method.

class DfsNode extends NodeImpl with Weighted {
var previous: DfsNode = _
var label:String = _
var nodeEdges: Set[Edge] = Set()

override def connectWith(node: Node): Edge = {
val newEdge=super.connectWith(node)
nodeEdges = nodeEdges + newEdge
newEdge
}

def -->(n2:Node):DfsEdge =connectWith(n2)

override def toString()= label+ " w:"+weight
}

Edges are simpler, just add the weight and override toString:
class DfsEdge(origin:Node, dest:Node) extends EdgeImpl(origin, dest) with Weighted {
override def toString()= from.label+"-->"+to.label+" w:"+weight
}

All are enclosed in the DfsGraph class extending the DirectedGraph class, now I can assign the concrete types for Node and Edge:
type Node= DfsNode
type Edge= DfsEdge

The DirectedGraph class has a addNode method. To keep the DSL-ish quality of the construction of the graph, I added the method addNewNode to take the node label as parameter
def addNewNode(l:String):Node={
val newNode=super.addNode
newNode.label=l
newNode
}

So we can write: graph addNewNode "nodeLabel".

The example looks like this:

object Example extends Application {

val graph = new DfsGraph

val n1=graph addNewNode "start"
val n2=graph addNewNode "n2"
val n3=graph addNewNode "n3"
val n4=graph addNewNode "n4"
val n5=graph addNewNode "n5"
val n6=graph addNewNode "end"

n1-->n2 weight=2
n1-->n3 weight=1
n2-->n4 weight=1
n3-->n4 weight=3
n2-->n5 weight=1
n4-->n6 weight=1
n5-->n6 weight=3

graph.shortestPath(n1,n6)
println("Path")
graph.pathToStart(n6).reverse.map(println(_))

}

Pretty easy to read, don't you think?
In the next post, I'll show you how we can use this algorithm to solve the "Abbott's revenge" type of maze. Ideally, I want to create an interpreter for the input, exploring Scala's features for creating language interpreters.
Here is the complete code:

package dfs2;

abstract class Graph {
type Edge
type Node <: NodeIntf
abstract class NodeIntf {
def connectWith(node: Node): Edge
}
def nodes: Set[Node]
def edges: Set[Edge]
def addNode: Node
}

abstract class DirectedGraph extends Graph {
type Edge <: EdgeImpl
class EdgeImpl(origin: Node, dest: Node) {
def from = origin
def to = dest
}

class NodeImpl extends NodeIntf {
self: Node =>
def connectWith(node: Node): Edge = {
val edge = newEdge(this, node)
edges = edges + edge
edge
}
}

protected def newNode: Node
protected def newEdge(from: Node, to: Node): Edge

var nodes: Set[Node] =Set()
var edges: Set[Edge] =Set()
def addNode: Node = {
val node = newNode
nodes = nodes + node
node
}
}

trait Weighted {
var weight= Float.PositiveInfinity
}
class DfsGraph extends DirectedGraph {
class DfsNode extends NodeImpl with Weighted {
var previous: DfsNode = _
var label:String = _
var nodeEdges: Set[Edge] = Set()

override def connectWith(node: Node): Edge = {
val newEdge=super.connectWith(node)
nodeEdges = nodeEdges + newEdge
newEdge
}

def -->(n2:Node):DfsEdge =connectWith(n2)

override def toString()= label+ " w:"+weight
}

class DfsEdge(origin:Node, dest:Node) extends EdgeImpl(origin, dest) with Weighted {
override def toString()= from.label+"-->"+to.label+" w:"+weight
}

def addNewNode(l:String):Node={
val newNode=super.addNode
newNode.label=l
newNode
}

type Node= DfsNode
type Edge= DfsEdge

protected def newEdge(from: Node, to: Node): Edge = new DfsEdge(from,to)
protected def newNode: Node = new DfsNode

def shortestPath(start: DfsNode, end: DfsNode) = {
var unvisited=nodes
start.weight=0
while (!unvisited.isEmpty) {
val vertx=min(unvisited)
vertx.nodeEdges.map(improveDistance(_))
unvisited=unvisited-vertx
}
}

def improveDistance(a:Edge) ={
if (a.from.weight+a.weight< a.to.weight) {
a.to.weight=a.from.weight+a.weight
a.to.previous=a.from
}
}

def min(nodes: Set[DfsNode]): DfsNode = {
nodes.reduceLeft((a:DfsNode,b:DfsNode)=>if (a.weight<b.weight) a else b )
}

def pathToStart(end:DfsNode):List[DfsNode] = {
if (end == null)
Nil
else
end :: pathToStart(end.previous)
}
}

13 comments:

fullWindsor said...

hey, this is all very interesting. i didn' know about this Scala language, and sounds very interesting to me. i will try to read more about it.

i'm programming in erlang here, in my new job. i love a couple of things about erlang, and miss quite a lot of things about java. so, something like a mashup between functional and OO sounds good.

i didn´t know about your blog neither, gab!

un abrazo,
camilo

Unknown said...

Bienvenido, Cam!
Erlang... nice! It's getting a lot of hype recently. What are you doing with it?

Sometimes I think I should have taken the Functional programming course. :)

Abrazo
Gab
PD: sabias que voy a ser papa'?
:D

Unknown said...
This comment has been removed by a blog administrator.
Unknown said...


Techsaga, as Software development company experts, deliver inspiring, eye-catching designs and measurable campaigns that connect with target audiences, boost online marketing and encourage business growth. We can help you meet your needs across a range of full service online marketing services. Are you looking for help with great content, SEO, PPC campaigns, a full digital marketing strategy, campaign, or something else? No matter what you need, our experts can help you.

Ram Kumar said...

Nice article, may explore scala variables and scala data types

Prancerio said...

Thank you for the Great Post!
Prancer is a pre-deployment and post-deployment multi-cloud platform for Infrastructure as Code pipeline and continuous compliance in cloud

website: https://www.prancer.io/

Mohamedsalim said...

Great blog about Web application Development Company. I really loved reading it and definitly share this with my friend too. Expecting more blogs from your side

JacobHarman said...
This comment has been removed by the author.
Anonymous said...

Hello! When I was a student I just hated checking my papers, essays etc because it took a lot of my time but never guaranteed a good grade. Fortunately, there are now many tools for checking your text. I recommend you take one of them and try out this free semicolon comma and colons test tool!

IndriLi said...

Prepositional phrase generator free is a cool and handy tool that will allow you to improve the quality of your text work! The tool is easy to use and will save you a lot of time and effort! It will check your text, identify errors related to prepositions and you can correct them quickly and without any problems! I am pleased with the results of his work, and I recommend him to you too!

GidLi said...

Check out the comma splice checker website for a lot of useful and interesting information and a specialized tool. This tool will let you know if you have used commas, semicolons and so on correctly. I am pleased with his results of work, because thanks to him I saved a lot of time!

Unknown said...

Work for your own pleasure and do not waste your precious time on tasks that can be easily handled by a special service. One of the taikhs can be considered semicolon checker free, which has long been in demand among copywriters and students who want to create really high-quality and beautiful texts in a short time.

AR said...

Thanks for the wonderful blog. I recently came across many blogs but this blog has unique information this blog. If you have any mobile app development then reach out mobile app development company in Chennai