I'm trying to write a function that accepts a certain type or any of its sub-types as one of its arguments, then returns a value of a type or any of its sub-types.
[<AbstractClass>]
type Spreader () =
abstract Fn : unit -> unit
type Fire () =
inherit Spreader ()
override self.Fn () = ()
type Disease () =
inherit Spreader ()
override self.Fn () = ()
let spread (spr:#Spreader) : #Spreader =
match spr with
| :? Fire -> Fire ()
| :? Disease -> Disease ()
| _ -> failwith "I don't get it"
Obviously, this doesn't work but you get what I'm trying to do.
At first, I implemented an abstract function in the Spreader type and overrode (overrided?) it in the sub-types, but that required upcasting, which I'm trying to avoid.
Is this doable? I'm looking into generics, but I've not quite got a grasp on their F# implementation.
EDIT 2010.07.08 1730 PST
Regarding the suggestion that I use discriminated unions, I'd tried that before. The problem I ran into was that any function that I defined as a member of the base type had to process every branch of the union. For example:
type strength = float32
type Spreader =
| Fire of strength
| Disease of strength
member self.Spread () =
match self with
| Fire str -> Fire str
| Disease str -> Disease str
member self.Burn () =
match self with
| Fire str -> Fire str
| _ -> failwith "Only fire burns"
The Spread function works fine here, but if I want Fire to Burn, then I have to provide for Disease, too, which makes no sense.
I want to allow for possible attempts by the user to do something illegal like trying to Disease.Burn, but I don't want to have to return a bunch of option types all over the place, e.g.:
member self.Burn () =
match self with
| Fire str -> Some(Fire str)
| _ -> None
I'd rather just leave the Burn function strictly to the Fire Spreader, and not even have it defined for the Disease Spreader.
Furthermore, this goes for properties, too; there are some members that I want Fire to have that don't make sense for Disease, and vice versa. Also, I'd like to be able to access a Spreader's strength value using dot notation, since I'm going to have other members that I access that way anyway, and it seems curiously redundant to have to define member.strength after it's already encapsulated in the object. ( e.g. | Disease str -> ...)
Another option, of course, is to simply separate the Spread and Burn functions from the Spreader type, but then I have to either (1) supply ugly upcasting or generics code for the functions (as others have described), or (2) have totally separate functions for Fire and Disease, which would suck in the case of Spread as I'd have to name them SpreadFire and SpreadDisease (since function overloading outside types is curiously not allowed).
As a noob to F#, I welcome all criticisms and suggestions. :)
EDIT 2010.07.09 0845 PST
Jon Harrop: "Why are you using augmentations and members?"
Because the types in my actual code are mathematics-intensive, so I'm precomputing certain values at initialization and storing them as members.
Jon Harrop: "Why do you want burn to apply to Speader types when it is only applicable to one?"
As I wrote, I don't want Burn to apply to Spreader. I want it to apply solely to Fire. I was torn, however, between implementing the function as a member of Spreader, and separating it from the Spreader type. (Inconsistency smell.)
Jon Harrop: "What is the purpose of your Fn member?"
Sorry, that was just extraneous code, please ignore it.
Jon Harrop: "Why did you use an abstract class instead of an interface?"
Code readability and efficiency, mostly. I hate having to upcast to an interface just to use one of its methods.
Jon Harrop: "Why did you define strength as an alias for float32?"
Code readability.
Jon Harrop: "What is the actual concrete problem you are trying to solve?!"
Code sharing for related types, while maintaining readability.
Interestingly, the solution that you offered is exactly the solution that I first tried (I've been at this F# thing for only a week or so), but I discarded it because I was uncomfortable with the idea of having to wrap my fundamental types in a DU as a work-around for not being able to overload functions defined outside type definitions. I'm just really paranoid about getting off on the wrong foot with fundamental types (in part due to the lamentable lack of F# refactoring in VS2010). Functional programming is a subject of great interest to me right now, and I'm pretty much just fishing around for understanding.
EDIT 2010.07.09 2230 PST
Actually, I/m starting not to like functional programming (really agree with the author at http://briancarper.net/blog/315/functional-programming-hurts-me -- if I see one more Fibonacci or factorial coding example in an F# tutorial or textbook, I'm going to stab the next nerd I find).
I was hoping that someone here would shoot back at my latest response (explaining why I am doing certain things) with the sort of scorn that Jon exhibited (below). I want to learn FP from the haughty elites, the coders who think their sh*t doesn't stink. Anyone gonna speak up?