Computation expressions provide the syntax, but not the abstraction power, of monads. Would be nice to have full monads, but still, CE are *extremely* useful to communicate clearly in code. For example, I am now doing some asynchronous socket processing including parsing input messages and sending output messages. After musing about it for a while, I figured that what I need is the Async monad enhanced to carry a state (I want this to propagate unparsed leftover input).
With this in place, I can define for example a parser that collects all input until a delimiter (for example, to read the HTTP request head before CR LF CR LF):
Naturally, there are different ways to write this but the CE syntax helps making the purpose clear, and also helps reasoning about the code.
module StateAsync =
type StateAsync<'S,'T> =
private { Run: 'S -> Async<'T * 'S> }
let LiftAsync a =
let g s =
async {
let! x = a
return (x, s)
}
{ Run = g }
let GetState =
let g s = async.Return(s, s)
{ Run = g }
let SetState s =
let g _ = async.Return((), s)
{ Run = g }
let Run { Run = f } s = f s
let Return x =
let g s = async.Return(x, s)
{ Run = g }
let Bind f x =
let g s = async.Bind(x.Run s, fun (v, s) -> (f v).Run s)
{ Run = g }
type StateAsyncBuilder = Do with
member this.Return x = Return x
member this.ReturnFrom(x: StateAsync<_,_>) = x
member this.Bind(x, f) = Bind f x
member this.Bind(x, f) = Bind f (LiftAsync x)
With this in place, I can define for example a parser that collects all input until a delimiter (for example, to read the HTTP request head before CR LF CR LF):
let readUntil (delimiter: string) (read: Async<string>) =
let rec loop acc (input: string) =
StateAsync.Do {
match input.IndexOf(delimiter) with
| -1 ->
let! x = read
return! loop (input :: acc) x
| k ->
let result =
input.Substring(k) :: acc
|> List.rev
|> String.concat ""
let leftover = input.Substring(k + delimiter.Length)
do! StateAsync.SetState leftover
return result
}
StateAsync.Do {
let! state = StateAsync.GetState
do! StateAsync.SetState ""
return! loop [] state
}
Naturally, there are different ways to write this but the CE syntax helps making the purpose clear, and also helps reasoning about the code.
Off topic, but I found it amusing that I was just writing something almost exactly like this. :)
Can you please elaborate a bit why CEs don't have the abstraction power of monads?
Aren't CEs a superset of monads that don't have to implement the axioms of a monad: [link:en.wikipedia.org]
Is it because of F#'s lack of type classes?
Aren't CEs a superset of monads that don't have to implement the axioms of a monad: [link:en.wikipedia.org]
Is it because of F#'s lack of type classes?
Haskell makes it trivial to define code that works in any monad, for example:
In F# this is not possible in general. Special cases that come close are limited, for example such code has to use inlining, does not benefit from separate compilation, and typically has unreadable inferred types.
Ability to abstract over the monad is what I have in mind when speaking of abstraction power. OCaml has the same abstraction power then via the module system:
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
In F# this is not possible in general. Special cases that come close are limited, for example such code has to use inlining, does not benefit from separate compilation, and typically has unreadable inferred types.
Ability to abstract over the monad is what I have in mind when speaking of abstraction power. OCaml has the same abstraction power then via the module system:
module type MONAD =
sig
type 'a t
val return : 'a -> 'a t
val bind : 'a -> ('a -> 'b t) -> 'b t
end
module MonadUtils(M : MONAD) =
struct
(* ... *)
end
I don't know if I understood the F# computation expressions correctly, but I guess it's a way to customize let, do, yield, return, etc. expressions and to delay the evaluation of an expression for the later time, isn't it?
yes. CEs are a way of doing something like aspect oriented programming. You can inject CE code at let, let!, for-do, while-do, try-catch-finally, etc. points of user written code (under the special F# syntax). There is a notion of monads in there but its not exactly the same.
I am not a Haskell expert, but Haskell has type classes which allows multiple monads to be composed together more easily. I don't think that you can do that in F#; each CE implementation is specific to one type of 'monad'.
I am not a Haskell expert, but Haskell has type classes which allows multiple monads to be composed together more easily. I don't think that you can do that in F#; each CE implementation is specific to one type of 'monad'.
Please don't mention Haskell again, because that confuses me in my efforts to anderstand F# CE.:)
Thanks for the example. I'll try to analyze it.
Aren't monads a Haskell concept and aren't they used to force the execution of a set of expressions in a certain order?
What would be monads in F# good for?
What would be monads in F# good for?
Having true monads in F# would be great for developing highly generic and reusable correct code. For example, take Parsec parser combinator library in Haskell. It is designed to work like most parsers do, consuming ready input. Now suppose you are making an IDE and want to parse input incrementally as it arrives from the user. All it takes to make Parsec parsers incremental is substituting a smart monad, no code rewriting required. All thanks to Parsec code being parametric in the monad.
Typically you don't need to create your own as the built-in ones or those that are part of a framework (such as WebSharper) are all that you would need.
Occasionally, you do have a need. I created one to manage COM object references from F#:
[link:fwaris.wordpress.com] .
I believe it important to *understand* how to create computational expressions because that will give you deeper insights into functional programming (consider it a rite of passage for F#).
When starting with computational expression look at the boilerplate implementation in the F# spec (section 6.3.10 ):
[link:research.microsoft.com]
Occasionally, you do have a need. I created one to manage COM object references from F#:
[link:fwaris.wordpress.com] .
I believe it important to *understand* how to create computational expressions because that will give you deeper insights into functional programming (consider it a rite of passage for F#).
When starting with computational expression look at the boilerplate implementation in the F# spec (section 6.3.10 ):
[link:research.microsoft.com]
I've been using F# for at least 2 years and haven't yet come across a need for creating my own computation expression. That could be due to a) the domain in which I work and b) the fact that you can invariably express the same concept other ways (in that sense, CEs are merely a syntactic convenience). I think they're less important in an impure functional language--obviously a language like Haskell couldn't do much without monads.
Topic tags
- f# × 3656
- compiler × 263
- functional × 199
- c# × 119
- websharper × 112
- classes × 96
- web × 94
- book × 84
- .net × 82
- async × 72
- parallel × 43
- server × 43
- parsing × 41
- testing × 41
- asynchronous × 30
- monad × 28
- ocaml × 26
- tutorial × 26
- haskell × 25
- workflows × 22
- html × 21
- linq × 21
- introduction × 19
- silverlight × 19
- wpf × 19
- fpish × 18
- collections × 14
- pipeline × 14
- templates × 12
- monads × 11
- opinion × 10
- reactive × 10
- plugin × 9
- scheme × 9
- sitelets × 9
- solid × 9
- basics × 8
- concurrent × 8
- deployment × 8
- how-to × 8
- python × 8
- complexity × 7
- javascript × 6
- jquery × 6
- lisp × 6
- real-world × 6
- workshop × 6
- xaml × 6
- conference × 5
- dsl × 5
- java × 5
- metaprogramming × 5
- ml × 5
- scala × 5
- visual studio × 5
- formlets × 4
- fsi × 4
- lift × 4
- sql × 4
- teaching × 4
- alt.net × 3
- aml × 3
- enhancement × 3
- reflection × 3
- blog × 2
- compilation × 2
- computation expressions × 2
- corporate × 2
- courses × 2
- cufp × 2
- enterprise × 2
- entity framework × 2
- erlang × 2
- events × 2
- f# interactive × 2
- fsc × 2
- google maps × 2
- html5 × 2
- http × 2
- interactive × 2
- interface × 2
- iphone × 2
- iteratee × 2
- jobs × 2
- keynote × 2
- list × 2
- mvc × 2
- numeric × 2
- obfuscation × 2
- oop × 2
- packaging × 2
- pattern matching × 2
- pipelines × 2
- rx × 2
- script × 2
- seq × 2
- sockets × 2
- stm × 2
- tcp × 2
- trie × 2
- type × 2
- type provider × 2
- xna × 2
- zh × 2
- .net interop × 1
- 2012 × 1
- abstract class × 1
- accumulator × 1
- active pattern × 1
- addin × 1
- agents × 1
- agile × 1
- android × 1
- anonymous object × 1
- appcelerator × 1
- architecture × 1
- array × 1
- arrays × 1
- asp.net 4.5 × 1
- asp.net mvc × 1
- asp.net mvc 4 × 1
- asp.net web api × 1
- aspnet × 1
- ast × 1
- b-tree × 1
- bistro × 1
- bug × 1
- camtasia studio × 1
- canvas × 1
- class × 1
- client × 1
- clojure × 1
- closures × 1
- cloud × 1
- cms × 1
- coding diacritics × 1
- color highlighting × 1
- combinator × 1
- confirm × 1
- constructor × 1
- continuation-passing style × 1
- coords × 1
- coursera × 1
- csla × 1
- css × 1
- data × 1
- database × 1
- declarative × 1
- delete × 1
- dhtmlx × 1
- discriminated union × 1
- distance × 1
- docs × 1
- documentation × 1
- dol × 1
- domain × 1
- du × 1
- eclipse × 1
- edsl × 1
- em algorithm × 1
- emacs × 1
- emotion × 1
- error × 1
- etw × 1
- euclidean × 1
- event × 1
- example × 1
- ext js × 1
- extension methods × 1
- extra × 1
- facet pattern × 1
- fantomas × 1
- fear × 1
- fp × 1
- frank × 1
- fsdoc × 1
- fsharp.core × 1
- fsharp.powerpack × 1
- fsharpx × 1
- function × 1
- functional style × 1
- gc × 1
- generic × 1
- geometry × 1
- getlastwin32error × 1
- google × 1
- group × 1
- hash × 1
- history × 1
- hosting × 1
- httpcontext × 1
- https × 1
- hubfs × 1
- ie 8 × 1
- if-doc × 1
- inheritance × 1
- installer × 1
- interpreter × 1
- io × 1
- ios × 1
- ipad × 1
- kendo × 1
- learning × 1
- licensing × 1
- macro × 1
- macros × 1
- maps × 1
- markup × 1
- marshal × 1
- math × 1
- metro style × 1
- micro orm × 1
- minimum-requirements × 1
- multidimensional × 1
- multithreading × 1
- mysql × 1
- mysqlclient × 1
- nancy × 1
- nested × 1
- nested loops × 1
- node × 1
- object relation mapper × 1
- object-oriented × 1
- offline × 1
- option × 1
- orm × 1
- osx × 1
- owin × 1
- paper × 1
- parameter × 1
- performance × 1
- persistent data structure × 1
- phonegap × 1
- pola × 1
- powerpack × 1
- prefix tree × 1
- principle of least authority × 1
- programming × 1
- projekt_feladat × 1
- protected × 1
- provider × 1
- ptvs × 1
- quant × 1
- quotations × 1
- range × 1
- raphael × 1
- razor × 1
- rc × 1
- real-time × 1
- reference × 1
- restful × 1
- round table × 1
- runtime × 1
- scriptcs × 1
- scripting × 1
- service × 1
- session-state × 1
- sitelet × 1
- stickynotes × 1
- stress × 1
- strong name × 1
- structures × 1
- tdd × 1
- template × 1
- tracing × 1
- tsunamiide × 1
- type inference × 1
- type providers × 1
- upload × 1
- vb × 1
- vb.net × 1
- vector × 1
- visual f# × 1
- visual studio 11 × 1
- visual studio shell × 1
- visualstudio × 1
- web api × 1
- webapi × 1
- windows 8 × 1
- windows-phone × 1
- winrt × 1
- xml × 1
|
Copyright (c) 2011-2012 IntelliFactory. All rights reserved. Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us |
Built with WebSharper |
why to bother to create them?
What is the main purpose of them?
Thanks and best regards.