How to write F# stm as >>= pipeline

388 views Asked by At

Can somebody please explain how to write this FSharpx stm as a pipeline?

    stm {
        let! allTops = readTVar tAllTops
        let! thisPlayerTops = mapM removeOtherPlayersScores allTops
        let! markedTops = mapM markAsNonEmpty thisPlayerTops

        return 
            markedTops 
            |> Seq.filter fst 
            |> Seq.map snd
            |> List.ofSeq
    } 

I'm thinking of haskell-like >>= pipelines.

Thanks!

UPDATE: A little bit of clarification in order to avoid the confusion:

I was thinking that one should be able to define the >>= operator in F# in terms of stm.Bind and stm.Return. I tried to do that myself by I got lost.

UPDATE2: After Thomas' answer I post the updated version which I think it looks pretty OK. If I understand correctly, because of lack of type-classes the operator >>= doesn't have the same power as in Haskell.

I agree that it's not idiomatic for F# but it's probably a good exercise.

    readTVar tAllTops
    >>= mapM removeOtherPlayersScores 
    >>= mapM markAsNonEmpty 
    >>= stm.Return >> Seq.filter fst  >> Seq.map snd >> List.ofSeq
    |> atomically

Thanks!

2

There are 2 answers

1
Tomas Petricek On BEST ANSWER

The >>= operator in Haskell is just a symbolic name for the bind operation, so you can define it in F# just as an alias for stm.Bind:

let (>>=) v f = stm.Bind(v, f)

Using the operator, you could rewrite the code as follows:

readTVar tAllTops >>= fun allTops ->
removeOtherPlayersScores allTops >>= fun thisPlayerTops ->
mapM markAsNonEmpty thisPlayerTops >>= fun markedTops ->
  markedTops 
  |> Seq.filter fst 
  |> Seq.map snd
  |> List.ofSeq
  |> stm.Return

This is certainly an interesting thing and a good way to learn about monads in F# (especially if you are coming from the Haskell background), but it is not an idiomatic style - the idiomatic style in F# is to use the computations explicitly.

One limitation of this approach (compared to Haskell) is that >>= is not polymorphic over monads, so you're not gaining anything. Also, I think that there is a general consensus that using the computation blocks is more readable (for F# developers)

2
Chris Taylor On

The main difference that an STM expression is converted to IO using atomically in Haskell, the difference syntax for binding monadic results (aka computation expressions) to names (using the <- operator) and that Haskell lists are lazy by default, which means that you don't need to use the Seq library (which, as far as I know, gives you lazy lists in F#).

atomically $ do
  allTops <- readTVar tAllTops
  thisPlayerTops <- mapM removeOtherPlayersScores allTops
  markedTops <- mapM markAsNonEmpty thisPlayerTops
  return (map snd . filter fst $ markedTops)