Talk:Monads/List monad

From Rosetta Code

Perhaps a specific task ?

It can help comparison across languages if there is a specific task with a readily intelligible motivation. Providing a specific set of inputs and expected outputs can make the code even more directly comparable.

One candidate (which would also allow comparison with the with the more syntactically-oriented List Comprehension task) would be to

  1. Write unit/return and bind for lists.
  2. Generate the cartesian product of, for example, [1..5] and [6..10]
  3. Obtain the list of all Pythagorean triples with elements between 1 and 25, by composing the application of a monadic triple-testing function (of type (Number, Number, Number) -> [(Number, Number, Number)] where the return value is the empty list if the triple is not Pythagorean) across the cartesian product of three lists (the ranges of possible values for (x, y, z) in the triples to be tested). (See the ES5 JavaScript example on this page).

F# example

The present F# example doesn't define a list monad:

<lang fsharp> // Monads/List monad . Nigel Galloway: March 8th., 2021 List.iter ((+) 1>>(*) 2>>printf "%d ") [3;4;5]; printfn "";; let pT n=[for i in 1..n do for g in i+1..n do for n in g+1..n do if i*i+g*g=n*n then yield(i,g,n)] Seq.iter(printf "%A ")(pT 25) let fN g=match g<10 with false->Error "is greater than 9"|_->Ok g let fG n=match n>5 with false->Error "is less than 6" |_->Ok n let valid n=n|>Result.bind fN|>Result.bind fG let test n=match valid(Ok n) with Ok g->printfn "%d is valid" g|Error e->printfn "Error: %d %s" n e [5..10]|>List.iter test </lang>

Output:
8 10 12
(3, 4, 5) (5, 12, 13) (6, 8, 10) (7, 24, 25) (8, 15, 17) (9, 12, 15) (12, 16, 20) (15, 20, 25)
Error: 5 is less than 6
6 is valid
7 is valid
8 is valid
9 is valid
Error: 10 is greater than 9

I'm not an F# user, so I'm not going to delete what's there. But there *is* apparently a very serviceable way of implementing the list monad in F# using computation expressions:

<lang fsharp> // The List Monad in F#

type ListMonad() =

  member o.Bind(  (m:'a list), (f: 'a -> 'b list) ) = List.concat( List.map f m )
  member o.Return(x) = [x]

let list = ListMonad()

let cartesian = list { let! x = [1..3]

                      let! y = [4..6]
                      return (x,y) }

printf "%A" cartesian </lang>

(from [1])

It might be good if someone more familiar with the wiki and with F# can confirm that this change is appropriate. NB I have checked that this code works, but the syntax highlighting here is getting confused.

More:

<lang fsharp> type ListMonad() =

  member o.Bind(  (m:'a list), (f: 'a -> 'b list) ) = List.concat( List.map f m )
  member o.Return(x) = [x]
  member o.Zero()    = []

let list = ListMonad()

let pyth_triples n = list { let! x = [1..n]

                           let! y = [x..n]
                           let! z = [y..n]
                           if x*x + y*y = z*z then return (x,y,z) }

printf "%A" (pyth_triples 100) </lang>

The task requirement is Construct a List Monad by writing the 'bind' function and the 'pure' (sometimes known as 'return') function for that Monad (or just use what the language already has implemented). The example just uses what the language already has implemented. 'let x=something" means refer to something.bind as x. In F# List is a ListMonad. F# uses yield rather than 'mu' 'pure' or 'return' see introducing yield.--Nigel Galloway (talk) 15:20, 14 September 2021 (UTC)
Hi. The way to implement new monads in F# is clearly with computation expressions [2]. You'll note that the table of functions on that page includes Bind and Return. We just need to fill in the right implementation for those functions. For the list monad it's straightforward, as the example I've given shows.
You do have a point that List Comprehensions are equivalent to the list monad. So maybe any discussion of implementing monads using computation expressions belongs on another page where it's actually essential to the functionality, say Monads/Writer monad. But by the same token, list comprehension examples surely belong at List comprehensions. After all, this is about showing off the specific language feature that the task specifies, isn't it? And this task is about defining new monads.
Re "yield", as you can see from Microsoft docs link above, Return is very much available and built into the interface of F#'s computation expressions, and there's no reason to prefer the alternative yield here, especially since the problem asks for 'return'.