Free Monad in F # with a common type of output

I am trying to apply the free monad template as described in F # for fun and profit in order to implement data access (for Microsoft Azure Table Storage)

Example

Suppose we have three database tables and three Tao Foo, Bar, Baz:

Foo Bar Baz key | col key | col key | col --------- --------- --------- foo | 1 bar | 2 | 

I want to select Foo with key = "foo" and Bar with key = "bar" to insert Baz with key = "baz" and col = 3

 Select<Foo> ("foo", fun foo -> Done foo) >>= (fun foo -> Select<Bar> ("bar", fun bar -> Done bar) >>= (fun bar -> Insert<Baz> ((Baz ("baz", foo.col + bar.col), fun () -> Done ())))) 

In interpreter function

  • Select results in a function call that takes key : string and returns obj
  • Insert leads to a function call that takes obj and returns unit

Problem

I defined two Select and Insert operations in addition to Done to complete the calculation:

 type StoreOp<'T> = | Select of string * ('T -> StoreOp<'T>) | Insert of 'T * (unit -> StoreOp<'T>) | Done of 'T 

To create a StoreOp chain, I am trying to implement the correct binding function:

 let rec bindOp (f : 'T1 -> StoreOp<'T2>) (op : StoreOp<'T1>) : StoreOp<'T2> = match op with | Select (k, next) -> Select (k, fun v -> bindOp f (next v)) | Insert (v, next) -> Insert (v, fun () -> bindOp f (next ())) | Done t -> ft let (>>=) = bindOp 

However, the f # compiler correctly warns me that:

 The type variable 'T1 has been constrained to be type 'T2 

For this bindOp implementation, the type is fixed in all calculations, so instead of:

 Foo > Bar > unit 

all i can express is:

 Foo > Foo > Foo 

How do I change the definition of StoreOp and / or bindOp to work with different types during computation?

+5
source share
1 answer

As Fedor mentioned in the comments, the problem is with the type declaration. If you want to compile it with a sacrifice such as security, you can use obj in two places - this at least shows where the problem is:

 type StoreOp<'T> = | Select of string * (obj -> StoreOp<'T>) | Insert of obj * (unit -> StoreOp<'T>) | Done of 'T 

I'm not quite sure what the two operations should simulate, but I think Select means that you are reading something (using the string key?) And Insert means that you are saving some value (and then continuing with unit ). So the data you save / think will be obj .

There are ways to make this type safe, but I think you will get a better answer if you explain what you are trying to achieve using a monadic structure.

Without knowing more, I think using free monads will make your code very dirty and hard to understand. F # is the first functional language, which means you can write data transformations in a good functional style using immutable data types and use imperative programming to load your data and save your results. If you work with a table store, why not just write normal imperative code to read data from a table store, transfer the results to a purely functional conversion, and then save the results?

+4
source

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


All Articles