- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers

# Help wanted with port of F# code to Scala.

Thu, 2012-01-05, 20:16

Hi,

I'm trying to port the following piece of F# code (actually a snippet

of a larger piece of more realistic working code):-

open System.Collections

open System.Collections.Generic

open System

[]

type Unbounded<'X> when 'X: comparison=

Finite of 'X

| NegativeInfinity

interface IComparable> with

member this.CompareTo another =

match this

, another with

Finite thisUnlifted

, Finite anotherUnlifted ->

compare thisUnlifted anotherUnlifted

| NegativeInfinity

, NegativeInfinity ->

0

| NegativeInfinity

, _ ->

-1

| _

, NegativeInfinity ->

1

The goal is to provide a lifted type in Scala, Unbounded, where X

offers the usual ordering semantics described by Ordered - in turn,

Unbounded adds in a lower bound of NegativeInfinity that is less

than all values of X.

So you can write 'let result = NegativeInfinity < Finite 2', 'printf

"%A" Finite 3.5 > Finite (-4.6)' etc in F#.

I started off by defining a union type in Scala and using covariance

on X - I was thinking of Option where None is effectively a

singleton object extending Option but compatible with

Option; in the same way NegativeInfinity would become an

Unbounded.

This was fine in isolation, but I ran into trouble getting the

resulting Unbounded to extend Ordered> due to the

covariance of X.

After several attempts, the best I've got so far is the rather messy

code below, which I'm certain could be improved upon.

Could anybody suggest a more concise / neater way of achieving my

goal?

Bonus points if the solution would solve the issue highlighted in the

line commented out in the test at the end!

I've added some further notes afterwards to describe some of the other

approaches I tried, but failed to get working.

Thanks in advance,

Gerard

import scala.math.Ordered

import org.scalatest.Suite

class Unbounded[X <% Ordered[X]] extends Ordered[Unbounded[X]] {

def compare(another: Unbounded[X]) = (this, another) match {

case (Finite(thisUnlifted), Finite(anotherUnlifted)) =>

thisUnlifted compare anotherUnlifted

case (NegativeInfinity(), NegativeInfinity()) => 0

case (NegativeInfinity(), _) => -1

case (_, NegativeInfinity()) => 1

}

}

case class Finite[X <% Ordered[X]](unlifted: X) extends Unbounded[X] {

}

case class NegativeInfinity[X <% Ordered[X]]() extends Unbounded[X]

object NegativeInfinity {

implicit def bridgeToAllCandidateTypes[X <% Ordered[X]](ni:

this.type) = new NegativeInfinity()

implicit def fallbackBridge(ni: this.type) = new

NegativeInfinity[Nothing]()

}

class TestSuite extends Suite {

def testOperations() = {

val negativeInfinity = NegativeInfinity

val twentyThree = Finite(23)

assert(negativeInfinity < twentyThree)

val fortyFive = Finite(45)

assert(twentyThree < fortyFive)

assert(!(twentyThree > fortyFive))

assert(Finite(23) == twentyThree)

assert(NegativeInfinity < Finite(45))

assert(Finite(45) > NegativeInfinity)

assert(NegativeInfinity == NegativeInfinity)

assert(!(NegativeInfinity[Int]() > NegativeInfinity()))

assert(!(NegativeInfinity > NegativeInfinity[Nothing]()))

assert(NegativeInfinity <= NegativeInfinity[Nothing])

assert(NegativeInfinity[Nothing] <= negativeInfinity)

assert(negativeInfinity <= NegativeInfinity[Nothing])

//assert(NegativeInfinity <= NegativeInfinity) // This is a

problem, try uncommenting it.

assert(NegativeInfinity < twentyThree)

}

}

object Main extends App {

(new TestSuite).execute()

}

Notes:

1. I wanted to have a simpler solution where Unbounded started off

like:

class Unbounded[+X <% Ordered[X]] extends Ordered[Unbounded[X]] {

^^^^ Covariant.

So I could make NegativeInfinity directly into a single case object:-

case class NegativeInfinity extends Unbounded[Nothing] {

That way I wouldn't need to implicit conversions - but this led to

problems with X being invariant within Ordered[Unbounded[X]], not to

mention in the definition of 'compare'.

2. Another approach I tried was to cut Unbounded[X] away from

Ordered[Unbounded[X]] by making it standalone, then adding in an

implicit conversion to Ordered[Unbounded[X]]:-

class Unbounded[+X <% Ordered[X]] {

implicit def bridge{Y >: X](unbounded: this.type) = new

Ordered[Unbounded[Y]] {

def compare(another: Unbounded[Y]) = // Delegating implementation

very similar to the one above.

The idea was to side-step the interaction between the covariance of X

in Unbounded[X] and the invariance in Ordered[Unbounded[X]] by putting

them into separate but related classes.

However I got into a mess with the type signatures, but perhaps

somebody can see how this could be made to work?

Fri, 2012-01-06, 23:01

#2
Compiler bug or obscure corner case? (Was: Help wanted with port

Hi,

I thought I'd repost this as I'm reasonably happy with the solution I

ended up with, except for one final detail - I see an odd compile

error when I comment out either of the two commented lines in the code

below.

It's got me completely foxed. :-(

(Look in the test at the end for 'COMPILE ERROR' in comments).

What seems odd is that there are several variations on the commented

line which all compile fine and do the right thing at runtime.

Can anybody explain whether this is a bug or some obscure corner case

in Scala's type inference?

I'm using Scala 2.9 as per an Eclipse plugin update from this week.

(BTW: if there is a more appropriate forum for these sorts of

questions, please let me know.)

Cheers,

Gerard

import scala.math.Ordered

import org.scalatest.Suite

abstract class Unbounded[X <% Ordered[X]] extends

Ordered[Unbounded[X]] {

def compare(another: Unbounded[X]) = (this, another) match {

case (Finite(thisUnlifted), Finite(anotherUnlifted)) =>

thisUnlifted compare anotherUnlifted

case (Foo(), Foo()) => 0

case (Foo(), _) => -1

case (_, Foo()) => 1

}

}

case class Finite[X <% Ordered[X]](unlifted: X) extends Unbounded[X] {

}

case class Foo[X <% Ordered[X]]() extends Unbounded[X] {

implicit def fakeCovarianceHack[Y <% Ordered[Y]](ni: this.type) =

Foo[Y]()

}

object NegativeInfinity extends Foo[Nothing] {

}

class TestSuite extends Suite {

def testOperations() = {

val negativeInfinity = NegativeInfinity

val fortyFive = Finite(45)

assert(negativeInfinity < fortyFive)

//assert(negativeInfinity < Finite(45)) // ****** COMPILE ERROR

WHEN UNCOMMENTED: Huh? ******

assert(NegativeInfinity < fortyFive)

//assert(NegativeInfinity < Finite(45)) // ****** COMPILE ERROR

WHEN UNCOMMENTED: Huh? ******

assert(Finite(45) > negativeInfinity)

assert(Finite(45) > NegativeInfinity)

def wrap(x:Int) = Finite(x)

assert(NegativeInfinity < wrap(45))

assert(wrap(45) > NegativeInfinity)

assert(NegativeInfinity < (Finite(45): Unbounded[Int]))

assert(NegativeInfinity < (Finite(45): Finite[Int]))

//********************************************

val twentyThree = Finite(23)

assert(negativeInfinity < twentyThree)

assert(twentyThree < fortyFive)

assert(!(twentyThree > fortyFive))

assert(Finite(23) == twentyThree)

//********************************************

assert(NegativeInfinity == NegativeInfinity)

assert(!(Foo[Int]() > Foo()))

assert(!(NegativeInfinity > Foo[Nothing]()))

assert(NegativeInfinity <= Foo[Nothing])

assert(Foo[Nothing] <= negativeInfinity)

assert(negativeInfinity <= Foo[Nothing])

assert(NegativeInfinity <= NegativeInfinity)

assert(!(NegativeInfinity > NegativeInfinity))

assert(NegativeInfinity < twentyThree)

}

}

object Main extends App {

(new TestSuite).execute()

}

Mon, 2012-01-09, 05:51

#3
Re: Compiler bug or obscure corner case? (Was: Help wanted with

Hi Gerard,

this looks certainly interesting!

I guess if you use negativeInfinity (which extends Foo[Nothing] -> Unbounded[Nothing]) on the left the argument type of the Ordered methods (<, >, ...) is already fixed to Nothing and if negativeInfinity is on the right it works because of Covariance ...

Just my guess ...

Bye,

Simon

this looks certainly interesting!

I guess if you use negativeInfinity (which extends Foo[Nothing] -> Unbounded[Nothing]) on the left the argument type of the Ordered methods (<, >, ...) is already fixed to Nothing and if negativeInfinity is on the right it works because of Covariance ...

Just my guess ...

Bye,

Simon

Ok, thinking about this, I don't really want to have X covariant in

Unbounded[X] - I just want to be able to write NegativeInfinity

without a type parameter when I use it, e.g. Finite(45) >

NegativeInfinity for a rather contrived example. (I use

NegativeInfinity as a special guaranteed lower bound when checking

through a sequence of X's - saves me from writing a type class

implementation providing a minimum over all values for each X, in case

anyone is wondering what this is for. In practice the X can be a

structured type where it can get fiddly defining a lower bound, so it

easier to just lift the type X rather than work directly within it.)

So I tried tidying things up based on where I got last time and have

golfed it to:-

import scala.math.Ordered

import org.scalatest.Suite

class Unbounded[X <% Ordered[X]] extends Ordered[Unbounded[X]] {

def compare(another: Unbounded[X]) = (this, another) match {

case (Finite(thisUnlifted), Finite(anotherUnlifted)) =>

thisUnlifted compare anotherUnlifted

case (Foo(), Foo()) => 0

case (Foo(), _) => -1

case (_, Foo()) => 1

}

}

case class Finite[X <% Ordered[X]](unlifted: X) extends Unbounded[X]

case class Foo[X <% Ordered[X]]() extends Unbounded[X] {

implicit def fakeCovarianceHack[Y <% Ordered[Y]](ni: this.type) =

Foo[Y]()

}

object NegativeInfinity extends Foo[Nothing] {

}

class TestSuite extends Suite {

def testOperations() = {

val negativeInfinity = NegativeInfinity

val twentyThree = Finite(23)

assert(negativeInfinity < twentyThree)

val fortyFive = Finite(45)

assert(twentyThree < fortyFive)

assert(!(twentyThree > fortyFive))

assert(Finite(23) == twentyThree)

assert(negativeInfinity < fortyFive)

//assert(negativeInfinity < Finite(45)) // COMPILE ERROR WHEN

UNCOMMENTED: Huh?

assert(NegativeInfinity < fortyFive)

//assert(NegativeInfinity < Finite(45)) // COMPILE ERROR WHEN

UNCOMMENTED: Huh?

assert(Finite(45) > NegativeInfinity)

assert(NegativeInfinity == NegativeInfinity)

assert(!(Foo[Int]() > Foo()))

assert(!(NegativeInfinity > Foo[Nothing]()))

assert(NegativeInfinity <= Foo[Nothing])

assert(Foo[Nothing] <= negativeInfinity)

assert(negativeInfinity <= Foo[Nothing])

assert(NegativeInfinity <= NegativeInfinity)

assert(!(NegativeInfinity > NegativeInfinity))

assert(NegativeInfinity < twentyThree)

}

}

object Main extends App {

(new TestSuite).execute()

}

For the sake of exposition in this post, I've changed the name of the

case class from NegativeInfinity to Foo to distinguish it from the

NegativeInfinity object I would actually use in client code.

This works, but now I've traded the compile problem in the original

piece of code for a new one - there seems to be some subtle difference

between a named value of type Finite[Int] and an expression of the

same type that prevents the commented out lines from type checking

when uncommented.

Could anybody shed some light on this, please?

Cheers,

Gerard