Created
October 31, 2012 10:15
-
-
Save rentalcustard/3986263 to your computer and use it in GitHub Desktop.
My experience of static typing.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MasterOfCeremonies { //I hate naming in potted examples | |
public void handle(Dog dog) { | |
dog.speak(); | |
} | |
} | |
class Dog { | |
public void speak() { | |
//something | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//And now I want handle to accept anything that can speak... | |
class MasterOfCeremonies { | |
public void handle(Speaker speaker) { | |
speaker.speak(); | |
} | |
} | |
interface Speaker { | |
void speak(); | |
} | |
class Dog implements Speaker { | |
public void speak() { | |
//something | |
} | |
} | |
class StandupComedian implements Speaker { | |
public void speak() { | |
//something else | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MasterOfCeremonies | |
def handle(speaker) | |
speaker.speak #no need to define an interface, anything that responds to speak accepted by this method. | |
end | |
end |
Totally don't need the type parameter on Speaker either:
class MasterOfCeremonies {
def handle[A <% Speaker](speaker : A) : Unit = {
speaker.speak
}
trait Speaker {
def speak : Unit;
}
implicit def dogCanSpeak(dog : Dog) : Speaker = {
return new Speaker {
override def speak : Unit = {
//implement the dog's speak method here
}
}
}
implicit def standupCanSpeak(standup : StandupComedian) : Speaker = {
return new Speaker {
override def speak : Unit = {
//implement the standup's speaking related functionality here
}
}
}
}
class Dog {
//dog-specific implementation
}
class StandupComedian {
//stand-up comedian specific implementation
}
Of course, you can also replicate the Ruby-ish duck-typing behaviour, with static checking, using a structural type:
class MasterOfCeremonies {
def handle(speaker : {def speak : Unit}) : Unit = {
speaker.speak
}
}
class Dog {
def speak : Unit = {
println("Woof")
}
}
class StandupComedian {
def speak : Unit = {
println("'That Bond villain came in my pub last night. Got drunk and behaved appalingly.' 'Javier Bardem?' 'No, he can come back when he's sober.'")
}
}
val mc = new MasterOfCeremonies
val dog = new Dog
val standup = new StandupComedian
mc.handle(dog)
mc.handle(standup)
...that way, compilation will fail if handle
is not passed an instance of a class that implements speak
, but gets around the need to have an actual named interface. However, this still requires you to re-open the classes that you want to implement 'Speak' though, so we still have the same problem as above (I actually just found out there's a name for this).
You could probably have the best of both worlds with an implicit conversion to a structural type, but this doesn't seem to work, sadly.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
....also I got the view bound syntax wrong. Try this, which actually compiles: