c# - Access to F# record base properties without using interface -
f# records cannot inherited, can implement interfaces. example, want create different controllers:
type controllertype = | basic | advanced1 | advanced1ram | advanced1rambattery | advanced2 // base abstract class type icontroller = abstract member rom : byte[] abstract member ``type`` : controllertype type basiccontroller = { rom : byte[] ``type`` : controllertype } interface icontroller member this.rom = this.rom member this.``type`` = this.``type`` type advancedcontroller1 = { ram : byte[] rom : byte[] ``type`` : controllertype } interface icontroller member this.rom = this.rom member this.``type`` = this.``type`` type advancedcontroller2 = { rommode : byte rom : byte[] ``type`` : controllertype } interface icontroller member this.rom = this.rom member this.``type`` = this.``type`` let init ``type`` = match ``type`` | basic -> { rom = array.zerocreate 0 ``type`` = basic } :> icontroller | advanced1 | advanced1ram | advanced1rambattery -> { ram = array.zerocreate 0 rom = array.zerocreate 0 ``type`` = ``type`` } :> icontroller | advanced2 -> { rommode = 0xffuy rom = array.zerocreate 0 ``type`` = ``type`` } :> icontroller
i have 2 questions:
- when create controller record, need upcast interface. there better way write
init
function above without:> icontroller
each record? - i tried discriminated unions somehow end writing interfance example. interface .net thing, how can rewrite example in functional way, composition rather inheritance?
answer first question: no, cannot rid of upcasting every time. f# doesn't automatic type coercion (which thing), , match
branches must have same type. thing coerce manually.
answer second question: discriminated unions represent "closed world assumption" - is, when know number of different cases upfront, , you're not interested in extending them later (your world "closed"). in case, can have compiler make sure working thing handles cases. super powerful applications.
on other hand, need design thing in such way can extended later, possibly external plugin. situation referred "open world assumption". in case, interfaces work. not way.
interfaces nothing more records of functions, exception of method genericity. if you're not interested in generic methods and you're not planning on downcasting specific implementations later (which bad thing anyway), can represent "open world" thing record of functions:
type controller = { ``type``: controllertype controlsomething: controllablething -> controlresult }
now can create different types of controllers providing different controlsomething
implementation:
let init ``type`` = match ``type`` | basic -> let rom = array.zerocreate 0 { ``type`` = basic controlsomething = fun c -> makecontrolresult c rom } | advanced1 | advanced1ram | advanced1rambattery -> let ram = array.zerocreate 0 let rom = array.zerocreate 0 { ``type`` = ``type`` controlsomething = fun c -> makecontrolresultwithram c rom ram } | advanced2 -> let rommode = 0xffuy let rom = array.zerocreate 0 { ``type`` = ``type`` controlsomething = fun c -> /* whatever */ }
incidentally, gets rid of upcasting, since of same type. incidentally, code smaller now, since don't have explicitly define different controllers own types.
q: wait, now, how access ram
, rom
, rommode
outside?
a: well, how going interface? going downcast interface specific implementation type, , access fields? if going that, you're "closed world", because handles icontroller
needs know implementation types , how work them. if case, better off discriminated union begin with. (like said above, downcasting not idea)
on other hand, if you're not interested in downcasting specific types, means you're interested in consuming functionality controllers implement (this whole idea of interfaces). if case, record of functions sufficient.
finally, if are interested in generic methods, have use interfaces, still don't have declare types, f# has inline interface implementations:
type controller = abstract member ``type``: controllertype abstract member genericmethod: 'a -> unit let init ``type`` = match ``type`` | basic -> let rom = array.zerocreate 0 { new controller member this.``type`` = basic member this.genericmethod x = /* whatever */ } // similar other cases
this little more verbose records, , can't amend them (i.e. no { ... ... }
syntax interfaces), if absolutely need generic methods, it's possible.
Comments
Post a Comment