开发者

Is F#'s implementation of monads unique with respect to the amount of keywords available to it?

开发者 https://www.devze.com 2023-03-05 14:00 出处:网络
I only know F#. I haven\'t learned the other functional programming languages. All the examples that I have seen for monads only describe the bind and unit methods. F# has lots of keywords (e.g. let!,

I only know F#. I haven't learned the other functional programming languages. All the examples that I have seen for monads only describe the bind and unit methods. F# has lots of keywords (e.g. let!, do!, etc.) that allow you to do 开发者_如何学编程different things within the same computational expression. This seemingly gives you more power than your basic bind and unit methods. Is this unique to F# or is it common across functional programming languages?


Yes, I think that the F# syntax for computation expressions is unique in that it provides direct syntactic support for different types of computations. It can be used for working with monoids, usual monads and also MonadPlus computations from Haskell.

I wrote about these in the introduction of my Master thesis. I believe it is quite readable part, so you can go to page 27 to read it. Anyway, I'll copy the examples here:

Monoid is used just for concatenating values using some "+" operation (Combine). You can use it for example for building strings (this is inefficient, but it demonstrates the idea):

type StringMonoid() =
  member x.Combine(s1, s2) = String.Concat(s1, s2)
  member x.Zero() = ""
  member x.Yield(s) = s

let str = new StringMonoid()

let hello = str { yield "Hello "
                  yield "world!" };;

Monads are the familiar example that uses bind and return operations of comptuation expressions. For example maybe monad represents computations that can fail at any point:

type MaybeMonad() =
  member x.Bind(m, f) =
    match m with Some(v) -> f v | None -> None
  member x.Return(v) = Some(v)

let maybe = new MaybeMonad()

let rec productNameByID() = maybe {
  let! id = tryReadNumber()
  let! prod = db.TryFindProduct(id)
  return prod.Name }

Additive monads (aka MonadPlus in Haskell) is a combination of the two. It is a bit like monadic computation that can produce multiple values. A common example is list (or sequence), which can implement both bind and combine:

type ListMonadPlus() =
  member x.Zero() = []
  member x.Yield(v) = [v]
  member x.Combine(a, b) = a @ b
  member x.Bind(l, f) = l |> List.map f |> List.concat

let list = new ListMonadPlus()

let cities = list {
  yield "York"
  yield "Orleans" }
let moreCities = list {
  let! n = cities
  yield n
  yield "New " + n }

// Creates: [ "York"; "New York"; "Orleans"; "New Orleans" ]

There are some additional keywords that do not directly correspond to any theoretical idea. The use keyword deals with resources and for and while can be used to implement looping. The sequence/list comprehension actually use for instead of let!, because that makes much more sense from the syntactic point of view (and for usually takes some sequence - although it may be e.g. asynchronous).


Monads are defined in terms of bind and unit operations (only). There are other structures which are defined by other operations (e.g. in Haskell, the MonadPlus typeclass has zero and plus operations - these correspond to Zero and Combine in F# computation expressions). As far as I know, F#'s computation builders are unique in terms of providing nice syntax for the wide range of operations that they support, but most of the operations are unrelated to monads.


F# binding forms ending in ! denote computation expressions, including let! use! do! yield! return!.

let! pat = expr in comp-expr         -- binding computation

do!  expr in comp-expr               -- sequential computation

use! pat = expr in comp-expr         -- auto cleanup computation

yield! expr                          -- yield computation

return! expr                         -- return computation

Computation expressions are used "for sequences and other non-standard interpretations of the F# expression syntax". These syntax forms offer ways to overload that syntax, for example, to encode monadic computations, or monoidal computations, and appear to be similar to e.g. the do-notation of Haskell, and corresponding (non-magic) bindings forms in that language.

So I would say that they support some overloading of syntax to support other interpretations of the expression syntax of the language, and this they have in common with many languages, including Haskell and OCaml. It is certainly a powerful and useful language feature.


References: The F# 2.0 Language Specification.


(Recall from memory, I may be off.)

While I think unit and bind are the typical basis for monads, I think maybe map and join for a different basis that I've seen in academic papers. This is kinda like how LINQ works in C# and VB, where the various from syntax desugars into Select or SelectMany which are similar to map and join. LINQ also has some 'extra' keywords, a little like F# though more ad-hoc (and mostly suited to querying enumerations/databases).

I don't know offhand of other functional languages like F# that effectively "lift" most of the control flow and other syntax into monads (well, "computation expressions", which may or may not be monads).

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号