Continuing the approach of gradually introducing functional, there are two ways in which I have found it very convenient to combine ("blend") DUs (discriminated unions) with an interface, implemented in classes.
The result is easily comprehended by an OO programmer. So I would teach these. THEN show purely functional alternatives.
[Posts 2A-2D] The first "blend" is "A class with a DU member".
Motivation: A DU is a struct, and therefore has no general way to add a data field to the DU as a whole. (Though there are some alternatives in certain situations, to be covered a bit later). Nor is it clear what a constructor would look like, if a DU had alternatives -AND- initialization values that needed to be passed to the constructor.
Example: "Decorating abstract syntax trees with line numbers" [link:cs.hubfs.net]
Here, in the author's mind, the DU is the main focus, but then he wants, off to the side in some manner, some "extra" information, that most of the program won't access. Here is a similar DU from my PEG metacompiler:
1 2 3
type Spacing1 = | Space of Space | Comment of Comment
This is a translation of grammar rule:
Spacing1 <- Space / Comment
The design has a number of different grammar rules, each with their corresponding DU.
So there is also:
1 2 3
type Space = | Blank of Whitespace // ' ' or '\t' | EOL of EndOfLine // EndOfLine
...etc...
If instead of a DU, the author were using a class hierarchy, he would add "extra" members to the common base class, and they would then be available for use where needed. The downside of the class approach is that a class would have to be defined for each alternative; a DU with 4 alternatives would be replaced by 1 base class + 4 sub classes = 5 classes total.
The point here is that the class approach requires extra definition coding, but doesn't require client code to change in the face of this design change -- a key benefit. (NOTE: certain other design changes would be smoother in the functional implementation.) Anyway, here we DIDN'T use the class approach, we used a DU, so we need to deal with that.
An OO programmer, faced with this new requirement, might refactor by wrapping the DU in a class. A functional programmer might wrap in a record. Again, my approach is to first show what an OO person might think to do.
Define a record for the functionality to be added:
1
type Position = {index: int; line: int; column: int}
Define an interface as the contract that a class can respond with Position:
1 2
type HasPosition = abstract position: Position
Define a wrapper class per DU:
1 2 3 4
type WSpacing1(du: Spacing1, position: Position) = member it.du = du with interface HasPosition with member it.position = position
...etc...
Each reference to a DU is now replaced with a reference to the corresponding wrapper:
1 2 3
type Spacing1 = | Space of WSpace | Comment of WComment
What's interesting about the code, is that each wrapper has a "du" field, that returns its DU, and a "position" field.
So usage, that before was passing around Spacing1, would now pass around wrapper classes, and match against "ob.du" when needed:
1 2 3 4
let test(wrapper:WSpacing1) = match wrapper.du with | Space sp -> "Spacing1.Space" | Comment com -> "Spacing1.Comment"
Here is the complete code listing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
#light // ---------- Extended Information: Position ---------- /// The functionality to be added. type Position = {index: int; line: int; column: int} /// The contract that a class can respond with Position. type HasPosition = abstract position: Position // ---------- Basic Types, with Wrappers ---------- type AChar = char type EndOfLine = char /// ----- Space & WSpace ----- type Space = | Whitespace of AChar // ' ' or tab | EOL of EndOfLine // EndOfLine type WSpace(du: Space, position: Position) = member it.du = du with interface HasPosition with member it.position = position /// ----- Comment & WComment ----- /// This is a single alternative DU, for consistency with the multi-alternative grammar rules. type Comment = Chars of AChar list type WComment(du: Comment, position: Position) = member it.du = du with interface HasPosition with member it.position = position /// ----- Spacing1 & WSpacing1 ----- /// This is a translation of grammar rule: /// Spacing1 <- Space / Comment type Spacing1 = | Space of WSpace | Comment of WComment type WSpacing1(du: Spacing1, position: Position) = member it.du = du with interface HasPosition with member it.position = position // ---------- Tests ---------- let ps msg = printfn "%s" msg let pam msg a = printfn "%s = %A" msg a let zero_position = {index= 0; line= 1; column= 1} let test1(ws1:WSpacing1) = match ws1.du with | Space sp -> "Spacing1.Space" |> ps | Comment com -> "Spacing1.Comment" |> ps (ws1 :> HasPosition).position |> pam "wrapper.position" let test() = let ws = new WSpace(' ' |> Whitespace, zero_position) let ws1 = new WSpacing1(ws |> Space, zero_position) test1(ws1) test() printf "----- Done: Press any key. -----" System.Console.ReadKey(false) |> ignore
Now lets see a pure functional version. Here, instead of defining an interface, and a set of wrapper classes, we simply define a set of wrapper records. For example:
1
type WSpace = {du: Space; position: Position}
Can't get more succinct than that!
One complication arises when we use these records. It is easiest to construct records when no two record types use the same field names. We want all the "position" fields to have the same name, since they are the same type. So we will have to tell F# which record we are working with. E.g., instead of saying:
1
let ws = {du=' ' |> Whitespace; position=zero_position}
we have to say:
1
let ws = {WSpace.du=' ' |> Whitespace; WSpace.position=zero_position}
Comparing the previous "class" version to this "record" version, here the type definitions are significantly more concise, but each construction ended up slightly wordier. Here is repeated the "class" construction for comparison:
1
let ws = new WSpace(' ' |> Whitespace, zero_position) // This is from previous example.
While I like having the "du=" and the "position=" to show which field is which; I'm not fond of prepending "WSpace." multiple places.
WISH: a syntax requiring the record type in only one place, e.g.:
1
WSpace{du=' ' |> Whitespace; position=zero_position}
Actual field access is just what we want:
1
ws1.position
Vs. the class version:
1
(ws1 :> HasPosition).position // This is from previous example.
NOTE: In C#, the class version's access would have been as concise as the F# record version.
Here is the complete listing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
#light // Code Sample: DUs in records // ---------- Extended Information: Position ---------- /// The functionality to be added. type Position = {index: int; line: int; column: int} // ---------- Basic Types, with Wrappers ---------- type AChar = char type EndOfLine = char /// ----- Space & WSpace ----- type Space = | Whitespace of AChar // ' ' or tab | EOL of EndOfLine // EndOfLine type WSpace = {du: Space; position: Position} /// ----- Comment & WComment ----- /// This is a single alternative DU, for consistency with the multi-alternative grammar rules. type Comment = Chars of AChar list type WComment ={du: Comment; position: Position} /// ----- Spacing1 & WSpacing1 ----- /// This is a translation of grammar rule: /// Spacing1 <- Space / Comment type Spacing1 = | Space of WSpace | Comment of WComment type WSpacing1 = {du: Spacing1; position: Position} // ---------- Tests ---------- let ps msg = printfn "%s" msg let pam msg a = printfn "%s = %A" msg a let zero_position = {index= 0; line= 1; column= 1} let test1(ws1:WSpacing1) = match ws1.du with | Space sp -> "Spacing1.Space" |> ps | Comment com -> "Spacing1.Comment" |> ps ws1.position |> pam "wrapper.position" let test() = let ws = {WSpace.du=' ' |> Whitespace; WSpace.position=zero_position} let ws1 = {WSpacing1.du=ws |> Space; WSpacing1.position=zero_position} test1(ws1) test() printf "----- Done: Press any key. -----" System.Console.ReadKey(false) |> ignore
Creating these extra wrapper entities, whether classes or records, was a bit of extra coding; are there other approaches?
One simple approach would have been to add the position field directly to each alternative in each DU; e.g.:
1 2 3 4
type Spacing1 = | Space of Space * Position | Comment of Comment * Position
I find it unappealing to have to repeat the same information on every branch. It also violates my sense of factoring: something that is logically shared, should only be defined in one place. However, it does avoid the need to define another type.
Lets see how it affects the code. All of our matches extracting the du information get changed on every branch, to mark the extra parameter (I'm not thrilled by such widespread change; but at least the changes are small, and the compiler will tell you where needed):
1 2 3 4
match ws1 with | Space (sp,_) -> ... | Comment (com,_) -> ...
Worse, position is no longer a simple "." access, but requires a full match:
1 2 3 4
match ws1 with | Space (_,position) | Comment (_,position) -> ...
The construction is tight, so that is one plus:
1 2
let ws = (' ', zero_position) |> Whitespace
Here is the complete code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
#light // Code Sample: DUs with extra field on every choice // ---------- Extended Information: Position ---------- /// The functionality to be added. type Position = {index: int; line: int; column: int} // ---------- Basic Types, with Extra field on every choice ---------- type AChar = char type EndOfLine = char /// ----- Space ----- type Space = | Whitespace of AChar * Position // ' ' or tab | EOL of EndOfLine * Position // EndOfLine /// ----- Comment ----- /// This is a single alternative DU, for consistency with the multi-alternative grammar rules. type Comment = Chars of AChar list * Position /// ----- Spacing1 ----- /// This is a translation of grammar rule: /// Spacing1 <- Space / Comment type Spacing1 = | Space of Space * Position | Comment of Comment * Position // ---------- Tests ---------- let ps msg = printfn "%s" msg let pam msg a = printfn "%s = %A" msg a let zero_position = {index= 0; line= 1; column= 1} let test1(ws1:Spacing1) = match ws1 with | Space (sp,_) -> "Spacing1.Space" |> ps | Comment (com,_) -> "Spacing1.Comment" |> ps match ws1 with | Space (_,position) | Comment (_,position) -> position |> pam "wrapper.position" let test() = let ws = (' ', zero_position) |> Whitespace let ws1 = (ws, zero_position) |> Space test1(ws1) test() printf "----- Done: Press any key. -----" System.Console.ReadKey(false) |> ignore
Other approaches? Thanks to Jon Harrop for pointing out that recursive structures have a natural place to hook in other information. That doesn't apply readily to the design I've been showing; here is a design where it would apply, and Jon's brief notes on it. NOTE: I have slightly edited the example he supplied to fit in with my previous examples.
I won't go into detail about this; if you feel comfortable reading what is below: congratulations, you are now a functional programmer!
I do believe this is the kind of technique one needs to eventually grasp, to be a "master" at functional programming. I am aiming at an easier target: "functional enough" to start an OO programmer on the road.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
#light // Code Sample: recursive DU, using generic type. // ---------- Extended Information: Position ---------- /// The functionality to be added. type position = {index: int; line: int; column: int} (*Jon Harrop says: Just untie the recursive knot so that you can inject any extra data you need at a later time. This is very easy and widely done in other functional languages: *) (*// ---------- Original recursive DU, without position info ---------- type expr = | Int of int | Add of expr * expr | Mul of expr * expr *) // ---------- Recursive DU, now generic ---------- // becomes a pair of types that are parameterized over their "children": type 'a expr = | Int of int | Add of 'a * 'a | Mul of 'a * 'a type 'a expr_pos = { expr: 'a expr_pos expr; pos: position } // Then you mimic the layout of the types in the functions that act upon them: let rec eval_expr eval = function | Int n -> n | Add(f, g) -> eval f + eval g | Mul(f, g) -> eval f * eval g let rec eval_pos eval (ep:'a expr_pos) = eval_expr (eval_pos eval) ep.expr // Once you're finished extending you just tie the recursive knot: let rec eval (ep:'a expr_pos) = eval_pos (eval_expr eval) ep;; // ---------- Tests ---------- let zero_position = {index= 0; line= 1; column= 1} // NOTE: Not bothering to set meaningful positions in this example. eval { expr= Add( {expr=Int 3; pos= zero_position}, {expr=Int 4; pos= zero_position} ); pos= zero_position }
Topic tags
- f# × 3705
- websharper × 1897
- compiler × 286
- functional × 201
- ui next × 139
- c# × 121
- classes × 97
- web × 97
- .net × 84
- book × 84
- async × 76
- ui.next × 67
- bug × 54
- core × 49
- website × 49
- server × 45
- parallel × 43
- ui × 43
- enhancement × 41
- parsing × 41
- testing × 41
- trywebsharper × 41
- typescript × 37
- html × 35
- javascript × 35
- owin × 35
- asynchronous × 30
- monad × 28
- ocaml × 28
- tutorial × 27
- warp × 27
- haskell × 26
- sitelet × 25
- linq × 22
- workflows × 22
- wpf × 20
- fpish × 19
- introduction × 19
- silverlight × 19
- sitelets × 19
- monodevelop × 17
- rpc × 17
- suave × 17
- piglets × 16
- collections × 15
- feature request × 15
- jquery × 15
- templates × 15
- getting started × 14
- pipeline × 14
- kendoui × 13
- reactive × 12
- 4.1.0.171 × 11
- monads × 11
- opinion × 10
- 4.0.190.100-rc × 9
- deployment × 9
- fixed × 9
- formlets × 9
- in × 9
- json × 9
- plugin × 9
- proposal × 9
- scheme × 9
- solid × 9
- basics × 8
- concurrent × 8
- highcharts × 8
- how-to × 8
- python × 8
- 4.1.1.175 × 7
- complexity × 7
- documentation × 7
- visual studio × 7
- 4.1.2.178 × 6
- lisp × 6
- real-world × 6
- released in 4.0.192.103-rc × 6
- remoting × 6
- resources × 6
- scala × 6
- websharper ui.next × 6
- workshop × 6
- xaml × 6
- 4.0.193.110 × 5
- 4.2.3.236 × 5
- aspnetmvc × 5
- authentication × 5
- azure × 5
- bootstrap × 5
- conference × 5
- dsl × 5
- formlet × 5
- java × 5
- list × 5
- metaprogramming × 5
- ml × 5
- released in Zafir.4.0.188.91-beta10 × 5
- sql × 5
- visualstudio × 5
- websharper.forms × 5
- zafir × 5
- 4.0.192.106 × 4
- 4.0.195.127 × 4
- 4.1.0.38 × 4
- 4.2.1.86 × 4
- 4.2.6.118 × 4
- css × 4
- example × 4
- extensions × 4
- fsi × 4
- fsx × 4
- html5 × 4
- jqueryui × 4
- lift × 4
- reflection × 4
- remote × 4
- rest × 4
- spa × 4
- teaching × 4
- template × 4
- websocket × 4
- wontfix × 4
- 4.0.196.147 × 3
- 4.1.0.34 × 3
- 4.1.6.207 × 3
- 4.2.1.223-beta × 3
- 4.2.11.258 × 3
- 4.2.4.114 × 3
- 4.2.4.247 × 3
- 4.2.5.115 × 3
- 4.2.6.253 × 3
- 4.2.9.256 × 3
- ajax × 3
- alt.net × 3
- aml × 3
- asp.net mvc × 3
- canvas × 3
- cloudsharper × 3
- compilation × 3
- database × 3
- erlang × 3
- events × 3
- extension × 3
- file upload × 3
- forums × 3
- inline × 3
- issue × 3
- kendo × 3
- macro × 3
- mono × 3
- msbuild × 3
- mvc × 3
- pattern × 3
- piglet × 3
- released in Zafir.4.0.187.90-beta10 × 3
- svg × 3
- type provider × 3
- view × 3
- 4.1.1.64 × 2
- 4.1.5.203 × 2
- 4.1.7.232 × 2
- 4.2.10.257 × 2
- 4.2.3.111 × 2
- 4.2.5.249 × 2
- android × 2
- asp.net × 2
- beginner × 2
- blog × 2
- chart × 2
- client × 2
- client server app × 2
- clojure × 2
- computation expressions × 2
- constructor × 2
- corporate × 2
- courses × 2
- cufp × 2
- d3 × 2
- debugging × 2
- direct × 2
- discriminated union × 2
- docs × 2
- elm × 2
- endpoint × 2
- endpoints × 2
- enterprise × 2
- entity framework × 2
- event × 2
- f# interactive × 2
- fable × 2
- flowlet × 2
- formdata × 2
- forms × 2
- fsc × 2
- google maps × 2
- hosting × 2
- http × 2
- https × 2
- iis 8.0 × 2
- install × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- jquery mobile × 2
- keynote × 2
- lens × 2
- lenses × 2
- linux × 2
- listmodel × 2
- mac × 2
- numeric × 2
- oauth × 2
- obfuscation × 2
- offline × 2
- oop × 2
- osx × 2
- packaging × 2
- pattern matching × 2
- performance × 2
- pipelines × 2
- q&a × 2
- quotation × 2
- reference × 2
- released in Zafir.4.0.185.88-beta10 × 2
- rx × 2
- script × 2
- security × 2
- self host × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- tutorials × 2
- type × 2
- url × 2
- var × 2
- websharper.charting × 2
- websharper4 × 2
- websockets × 2
- wig × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- 4.0.194.126 × 1
- 4.1.3.184 × 1
- 4.1.4.189 × 1
- 4.2.0.214-beta × 1
- 4.2.12.259 × 1
- 4.2.2.231-beta × 1
- 4.2.8.255 × 1
- Canvas Sample Example × 1
- DynamicStyle Animated Style × 1
- Fixed in 4.0.190.100-rc × 1
- Released in Zafir.UI.Next.4.0.169.79-beta10 × 1
- SvgDynamicAttribute × 1
- WebComponent × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- actor × 1
- addin × 1
- agents × 1
- aggregation × 1
- agile × 1
- alter session × 1
- animation × 1
- anonymous object × 1
- apache × 1
- api × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net core × 1
- asp.net integration × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- attributes × 1
- authorization × 1
- b-tree × 1
- back button × 1
- badimageformatexception × 1
- bash script × 1
- batching × 1
- binding-vars × 1
- bistro × 1
- body × 1
- bundle × 1
- camtasia studio × 1
- cas protocol × 1
- charts × 1
- clarity × 1
- class × 1
- cli × 1
- clipboard × 1
- clojurescript × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- color zones × 1
- combinator × 1
- combinators × 1
- compile × 1
- compile code on server × 1
- config × 1
- confirm × 1
- content × 1
- context × 1
- context.usersession × 1
- continuation-passing style × 1
- coords × 1
- cordova × 1
- cors × 1
- coursera × 1
- cross-domain × 1
- csla × 1
- current_schema × 1
- custom content × 1
- data × 1
- data grid × 1
- datetime × 1
- debug × 1
- declarative × 1
- delete × 1
- devexpress × 1
- dhtmlx × 1
- dictionary × 1
- directattribute × 1
- disqus × 1
- distance × 1
- do binding × 1
- doc elt ui.next upgrade × 1
- docker × 1
- dojo × 1
- dol × 1
- dom × 1
- domain × 1
- du × 1
- duf-101 × 1
- dynamic × 1
- eastern language × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- enums × 1
- error × 1
- etw × 1
- euclidean × 1
- eventhandlerlist × 1
- examples × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- failed to translate × 1
- fake × 1
- fantomas × 1
- fear × 1
- float × 1
- form × 1
- form-data × 1
- forum × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- fsunit × 1
- function × 1
- functional style × 1
- game × 1
- games × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- getting-started × 1
- google × 1
- google.maps × 1
- grid × 1
- group × 1
- guide × 1
- hash × 1
- headers × 1
- hello world example × 1
- heroku × 1
- highchart × 1
- history × 1
- how to × 1
- html-templating × 1
- http405 × 1
- httpcontext × 1
- hubfs × 1
- i18n × 1
- ie 8 × 1
- if-doc × 1
- iis × 1
- image × 1
- images × 1
- inheritance × 1
- initialize × 1
- input × 1
- install "visual studio" × 1
- installer × 1
- int64 × 1
- interfaces × 1
- internet explorer × 1
- interop × 1
- interpreter × 1
- io × 1
- iobservable × 1
- ios × 1
- iot × 1
- ipad × 1
- isomorphic × 1
- javascript optimization × 1
- javascript semanticui resources × 1
- jquery-plugin × 1
- jquery-ui × 1
- jquery-ui-datepicker × 1
- js × 1
- kendo datasource × 1
- kendochart × 1
- kendoui compiler × 1
- knockout × 1
- l10n × 1
- learning × 1
- library × 1
- libs × 1
- license × 1
- licensing × 1
- lineserieszonescfg × 1
- local setting × 1
- localization × 1
- logging × 1
- loop × 1
- macros × 1
- mailboxprocessor × 1
- mapping × 1
- maps × 1
- markerclusterer × 1
- markup × 1
- marshal × 1
- math × 1
- mathjax × 1
- message × 1
- message passing × 1
- message-passing × 1
- meta × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- mix × 1
- mobile installation × 1
- mod_mono × 1
- modal × 1
- module × 1
- mouseevent × 1
- mouseposition × 1
- multidimensional × 1
- multiline × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- native × 1
- nested × 1
- nested loops × 1
- node × 1
- nunit × 1
- object relation mapper × 1
- object-oriented × 1
- om × 1
- onboarding × 1
- onclick × 1
- optimization × 1
- option × 1
- orm × 1
- os x × 1
- output-path × 1
- override × 1
- paper × 1
- parameter × 1
- persistence × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- post × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- privacy × 1
- private × 1
- profile × 1
- programming × 1
- project × 1
- project euler × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- proxy × 1
- ptvs × 1
- public × 1
- pure f# × 1
- purescript × 1
- qna × 1
- quant × 1
- query sitelet × 1
- question × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- reactjs × 1
- real-time × 1
- ref × 1
- region × 1
- released in 4.0.190.100-rc × 1
- reporting × 1
- responsive design × 1
- rest api × 1
- rest sitelet × 1
- restful × 1
- round table × 1
- router × 1
- routing × 1
- rpc reverseproxy × 1
- runtime × 1
- sales × 1
- sample × 1
- sampleapp × 1
- scriptcs × 1
- scripting × 1
- search × 1
- self hosted × 1
- semanticui × 1
- sequence × 1
- serialisation × 1
- service × 1
- session-state × 1
- sharepoint × 1
- signals × 1
- sitelet website × 1
- sitelet.protect × 1
- sitlets × 1
- slickgrid × 1
- source code × 1
- sqlentityconnection × 1
- ssl × 1
- standards × 1
- static content × 1
- stickynotes × 1
- streamreader × 1
- stress × 1
- strong name × 1
- structures × 1
- submitbutton × 1
- subscribe × 1
- svg example html5 websharper.ui.next × 1
- sweetalert × 1
- system.datetime × 1
- system.reflection.targetinvocationexception × 1
- table storage × 1
- targets × 1
- tdd × 1
- templates ui.next × 1
- templating × 1
- text parsing × 1
- three.js × 1
- time travel × 1
- tls × 1
- tooltip × 1
- tracing × 1
- tsunamiide × 1
- turkish × 1
- twitter-bootstrap × 1
- type erasure × 1
- type inference × 1
- type providers × 1
- type-providers × 1
- typeprovider × 1
- ui next forms × 1
- ui-next × 1
- ui.next jqueryui × 1
- ui.next charting × 1
- ui.next formlets × 1
- ui.next forms × 1
- ui.next suave visualstudio × 1
- ui.next templating × 1
- unicode × 1
- unittest client × 1
- upload × 1
- usersession × 1
- validation × 1
- vb × 1
- vb.net × 1
- vector × 1
- view.map × 1
- visal studio × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio 2012 × 1
- visual studio shell × 1
- vs2017 compiler zafir × 1
- vsix × 1
- web api × 1
- web-scraping × 1
- webapi × 1
- webcomponents × 1
- webforms × 1
- webgl × 1
- webrtc × 1
- webshaper × 1
- websharper async × 1
- websharper codemirror × 1
- websharper f# google × 1
- websharper forms × 1
- websharper reactive × 1
- websharper rpc × 1
- websharper sitelets routing × 1
- websharper warp × 1
- websharper-interface-generator × 1
- websharper.chartsjs × 1
- websharper.com × 1
- websharper.exe × 1
- websharper.owin × 1
- websharper.ui.next × 1
- websharper.ui.next jquery × 1
- websockets iis × 1
- why-websharper × 1
- windows 7 × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- www.grabbitmedia.com × 1
- xamarin × 1
- xml × 1
- yeoman × 1
- yield × 1
- zafir beta × 1
- zafir websharper4 × 1
- zarovizsga × 1
![]() |
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy |
Built with WebSharper |
[Taken from my post of the same name on F# mail list]
What would smooth the transition of a C# programmer to F#?
While some people here advocate "shock treatment" -- functional is a
different way of thinking so throw away your OO preconceptions and start
over without classes -- that approach (IMHO) is unlikely to appeal to anyone beyond
the highly motivated.
I want to discuss an alternate approach: "augment your bag of tricks with
some new ones".
This approach is designed to make the way as smooth as possible.
Begin by showing how to do in F# what you already know how to do in C#.
Take a number of simple C# fragments, and re-write them in F#, showing each
of the features that map 1:1. Start with C# 2.0 features only.
Early on, will have to discuss mutable vs. non-mutable values, since values
are mutable by default in C#. Show why non-mutable values are a good thing.
Then start introducing new features. Justify each feature by starting with a
design goal, solving it in C# as best as possible. Then show how using one
new feature of F# results in simpler code. NOTE: At the same time, can show
the migration from C# 2.0 to C# 3.0, where 3.0 adds a new
functionally-inspired feature similar to the F# one.
Start with features that are easy for an OO programmer to grasp:
* local functions.
* tuples.
* discriminated union & match.
* sequence comprehension & yield.
* functions as parameters.
Rather than insisting on a pure functional style, show how even a partial
rewrite using one of these features simplifies a given code snippet.
In particular, I have found the following "blended functional + OO" style to
be useful:
Define interfaces, and implement classes which implement those interfaces.
Use a DU whenever there is "choice between alternatives". Show how a DU is a
powerful way to work with several related classes, rather than deepening the
inheritance hierarchy. That is, encourage a "flat" OO style, where each
class directly implements interfaces, but does not inherit from other
classes. Have an interface hierarchy, but not a class hierarchy.
NOTE: My insistence on using even this limited amount of OO appears to be
controversial, based on reactions from experienced functional programmers.
Only after all this solid ground is established, would I demonstrate the
power of (advanced / higher-order / more abstract) constructs:
* currying
* list-folding
* replacing iteration with recursion
* functions that return functions
* workflow
~TMSteve