How to implement List Monad (calculation expression) with a condition?

I'm trying to figure out how to use F # evaluation expressions, and that of course puzzles me.

The following example makes some sense to me.

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

let list = ListMonad()

let test = 
    list {
        let! x = [ 1 .. 10]
        let! y = [2 .. 2 .. 20]
        return (x,y)
    }

My question is: how would you add a condition to this calculation expression? In particular, how would you modify it to return a list of elements only when the value of x is strictly less than the value of y? (Do not filter it afterwards).

+4
source share
2 answers

Since calculation expressions can be parameterized , you can try something like this first:

let filterAndCollect (pred : 'a -> 'b -> bool) (f : 'a -> 'b list) (m : 'a list) =
    let f' a = [ for b in f a do if pred a b then yield b ]
    List.collect f' m

type FilteringListMonad(pred) =
    member o.Bind(  (m:'a list), (f: 'a -> 'b list) ) = filterAndCollect pred f m
    member o.Return(x) = [x]

let filteredList = FilteringListMonad(fun x y -> x < y)

let test2 =
    filteredList {
        let! x = [1 .. 10]
        let! y = [2 .. 2 .. 20]
        return (x,y)
    }

However, this fails with a type error for the tuple (x,y):

, 'int', ''a * 'b'

: y x < y FilteringListMonad :

, . 'a ''b'.

1 let! x = [1 .. 10] :

, . 'b 'int'.

, (a 'b list) int list, int * int list. , . .. , , 'b, , int * int, , 'b, :

let filterAndCollect (pred : 'b -> bool) (f : 'a -> 'b list) (m : 'a list) =
    let f' a = [ for b in f a do if pred b then yield b ]
    List.collect f' m

type FilteringListMonad(pred) =
    member o.Bind(  (m:'a list), (f: 'a -> 'b list) ) = filterAndCollect pred f m
    member o.Return(x) = [x]

let filteredList = FilteringListMonad(fun (x:int,y:int) -> x < y)

let test2 =
    filteredList {
        let! x = [ 1 .. 10]
        let! y = [2 .. 2 .. 20]
        return (x,y)
    }

, . F # " , System.IComparable, int s, , , ,

, 'System.IComparable', 'int'.

int .

+6

OP , , , F #

ReturnFrom Zero :

type ListMonad() =
   member o.Bind        (m, f)  = List.collect f m
   member o.Return      x       = [x]
   member o.ReturnFrom  l       = l : _ list
   member o.Zero        ()      = []

let listM = ListMonad()

ReturnFrom , return! [], . Zero , else Zero.

:

let test = 
  listM {
    let! x = [ 1 .. 10]
    let! y = [2 .. 2 .. 20]
    if x % y = 0 then
      return (x,y)
//  By defining .Zero F# implicitly adds else branch if not defined
//  else
//    return! []
  }

F # :

let testEq = 
  [ 1 .. 10] 
  |> List.collect 
      (fun x -> 
        [2 .. 2 .. 20] 
        |> List.collect (fun y -> if x % y = 0 then [x,y] else [])
      )
+4

Source: https://habr.com/ru/post/1675972/


All Articles