The one giant robot every programmer should know and love! Meet Big O!
For all your algorithmic complexity needs. And any giant mecha in need of a good pounding.
And now, back to your regularly scheduled blog post…
Inspired both by Mark Seemann's excellent blog post and by my ongoing campaign to introduce functional programming techniques to 15below developers who aren't familiar with them yet, I decided it was time to run a mini-series on applying good principles like SOLID in a functional world.
We run weekly one hour "Developer Education" sessions. For this series I started with a badly written piece of code (it came naturally, given I had limited prep time…) in a style of someone who has kind of heard about SOLID and functional programming:
SOLID: "So, eh, I need some interfaces and things. Concrete bad, interface good. In wonder what the whole DI thing is?"
And then we had a open house suggesting modifications to the code where I made the changes live as we went along. In this first session, we only got as far as making the code a bit more solid - tune back in next month for the make it functional session..
Deciding that we needed a more interesting example domain than sending emails for once, I decided to go the whole hog. Below you'll find the before and after versions of "HeavyGearSOLID"'s unit representation code.
Because everything is better with giant robots.
The session was a fun change of pace from other things we've done, and sparked off a nice bit of discussion (although not as much as I hoped… heckle more, 15below people!). I'm quite looking forward to the next part of the series.
namespace``メタルギアソリッド``openSystemopenUtilstypeIMecha=abstractWalk:Direction*int->IMechaabstractJump:Direction*int->IMechaabstractFly:Direction*int->IMechaabstractPosition:int*intabstractHits:intabstractDestroyed:boolabstractDodge:intabstractArmour:intabstractTakeDamage:int->IMechaabstractFireCannon:IMecha*int->IMechaabstractFireMissiles:IMecha*int->IMechatypeGiantRobo(position)=let_position=refpositionlet_hits=ref100let_destroyed=reffalselet_dodge=ref5let_armour=ref20letrand=Random()interfaceIMechawithmemberx.Walk(dir,distance)=letiMecha=x:>IMechaifdistance>4thenfailwith"GiantRobo is slow!"else_position:=Move(!_position)dirdistanceiMechamemberx.Jump(_,_)=raise<|NotImplementedException("GiantRobo can't jump")memberx.Fly(_,_)=raise<|NotImplementedException("GiantRobo can't fly")memberx.Position=!_positionmemberx.Hits=!_hitsmemberx.Destroyed=!_destroyedmemberx.Dodge=!_dodgememberx.Armour=!_armourmemberx.TakeDamagedamage=_hits:=!_hits-damage_destroyed:=!_hits<=0x:>IMechamemberx.FireCannon(target,roll)=ifroll>target.Dodgethentarget.TakeDamage(max0(60-target.Armour))elsetargetmemberx.FireMissiles(target,roll)=raise<|NotImplementedException("Giant Robo has no missiles")typeITransformingMecha=inheritIMechaabstractMode:stringabstract``Transform!``:string->ITransformingMechatypeVF1(position)=let_position=refpositionlet_hits=ref50let_destroyed=reffalselet_dodge=ref10let_armour=ref10let_mode=ref"Battroid"interfaceITransformingMechawithmemberx.Walk(dir,distance)=letiMecha=x:>IMechaif!_mode="Fighter"thenfailwith"No legs in Fighter mode!"ifdistance>6thenfailwith"VF-1 isn't that fast!"else_position:=Move(!_position)dirdistanceiMechamemberx.Jump(dir,distance)=letiMecha=x:>IMechaif!_mode="Fighter"thenfailwith"Jumping in Fighter mode makes no sense!"letmaxDistance=match!_modewith|"GERWALK"->8|"Battroid"->6|_->failwith"No good"ifdistance>maxDistancethenfailwith"VF-1 isn't that fast!"else_position:=Move(!_position)dirdistanceiMechamemberx.Fly(dir,distance)=letiMecha=x:>IMechaif!_mode="Battroid"thenfailwith"Battroid mode can't fly"letmaxDistance=match!_modewith|"Fighter"->20|"GERWALK"->15|_->failwith"No good"ifdistance>maxDistancethenfailwith"VF-1 isn't that fast!"else_position:=Move(!_position)dirdistanceiMechamemberx.Position=!_positionmemberx.Hits=!_hitsmemberx.Destroyed=!_destroyedmemberx.Dodge=!_dodgememberx.Armour=!_armourmemberx.TakeDamagedamage=_hits:=!_hits-damage_destroyed:=!_hits<=0x:>IMechamemberx.FireCannon(target,roll)=ifroll>target.Dodgethentarget.TakeDamage(max0(20-target.Armour))elsetargetmemberx.FireMissiles(target,roll)=ifroll>(target.Dodge/2)thentarget.TakeDamage(max0(60-target.Armour))elsetargetmemberx.Mode=!_modememberx.``Transform!``mode=matchmodewith|"Fighter"->_mode:="Fighter"|"GERWALK"->_mode:="GERWALK"|"Battroid"->_mode:="Battroid"|_->failwith"Not a valid VF-1 mode"x:>ITransformingMecha
namespace``メタルギアソリッド``openSystemopenUtilstypeLocation={Position:int*intAltitude:int}typeIDestructable=abstractArmour:intabstractDodge:intabstractHits:intabstractDestroyed:boolabstractTakeDamage:int->IDestructabletypeIWalk=abstractWalk:Direction*int->LocationtypeIJump=abstractJump:Direction*int->LocationtypeIFly=abstractFly:Direction*int->LocationtypeIHazWeapon=abstractFire:IDestructable*int->IDestructabletypeIHazWeapons=abstractWMDs:List<IHazWeapon>typeIHazCannon=inheritIHazWeapontypeIHazMissiles=inheritIHazWeapontypeNormalDestructable(hits,dodge,armour)=let_hits=ref100let_destroyed=reffalselet_dodge=ref5let_armour=ref20interfaceIDestructablewithmemberx.Hits=!_hitsmemberx.Destroyed=!_destroyedmemberx.Dodge=!_dodgememberx.Armour=!_armourmemberx.TakeDamagedamage=_hits:=!_hits-damage_destroyed:=!_hits<=0x:>IDestructabletypeGiantRobo(position,destructable:#IDestructable)=let_position=refpositionmemberx.Position=!_positioninterfaceIWalkwithmemberx.Walk(dir,distance)=ifdistance>4thenfailwith"GiantRobo is slow!"else{Position=Move(!_position)dirdistance;Altitude=0}interfaceIDestructablewithmemberx.Hits=destructable.Hitsmemberx.Destroyed=destructable.Destroyedmemberx.Dodge=destructable.Dodgememberx.Armour=destructable.Armourmemberx.TakeDamagedamage=destructable.TakeDamagedamageinterfaceIHazWeaponswithmemberx.WMDs=[{newIHazCannonwithmemberx.Fire(target,roll)=ifroll>target.Dodgethentarget.TakeDamage(max0(60-target.Armour))elsetarget}]typeITransforming=abstractMode:stringabstract``Transform!``:string->ITransformingtypeVF1(position)=let_position=refpositionlet_mode=ref"Battroid"memberx.Position=!_positioninterfaceIWalkwithmemberx.Walk(dir,distance)=if!_mode="Fighter"thenfailwith"No legs in Fighter mode!"ifdistance>6thenfailwith"VF-1 isn't that fast!"else{Position=Move(!_position)dirdistance;Altitude=0}interfaceIJumpwithmemberx.Jump(dir,distance)=if!_mode="Fighter"thenfailwith"Jumping in Fighter mode makes no sense!"letmaxDistance=match!_modewith|"GERWALK"->8|"Battroid"->6|_->failwith"No good"ifdistance>maxDistancethenfailwith"VF-1 isn't that fast!"else{Position=Move(!_position)dirdistance;Altitude=0}interfaceIFlywithmemberx.Fly(dir,distance)=if!_mode="Battroid"thenfailwith"Battroid mode can't fly"letmaxDistance=match!_modewith|"Fighter"->20|"GERWALK"->15|_->failwith"No good"ifdistance>maxDistancethenfailwith"VF-1 isn't that fast!"else{Position=Move(!_position)dirdistance;Altitude=0}interfaceIHazWeaponswithmemberx.WMDs=[{newIHazCannonwithmemberx.Fire(target,roll)=ifroll>target.Dodgethentarget.TakeDamage(max0(20-target.Armour))elsetarget};{newIHazMissileswithmemberx.Fire(target,roll)=ifroll>(target.Dodge/2)thentarget.TakeDamage(max0(60-target.Armour))elsetarget}]interfaceITransformingwithmemberx.Mode=!_modememberx.``Transform!``mode=matchmodewith|"Fighter"->_mode:="Fighter"|"GERWALK"->_mode:="GERWALK"|"Battroid"->_mode:="Battroid"|_->failwith"Not a valid VF-1 mode"x:>ITransforming