Dedicated Computation Expressions like State Builder #388
Replies: 16 comments
-
Or you could use the available state-monad methods from F#+ to implement your CE? |
Beta Was this translation helpful? Give feedback.
-
As another workaround, yes, but it does not change the fact that you still need to roll your own |
Beta Was this translation helpful? Give feedback.
-
I am not sure, but I don't think F#+ defines any computation expressions aside from the generic ones you mentioned. When you said: I'm not sure 100% but I think the rationale is that https://fsprojects.github.io/FSharpPlus/type-state.html |
Beta Was this translation helpful? Give feedback.
-
I guess the reasoning for wanting a specific |
Beta Was this translation helpful? Give feedback.
-
While @adz is 100% right, I still fail to see a reason from the user perspective not to have some specialized CEs. As F#+ developer there is a technical limitation from F# (no higher kinds) that prevents us to re-use the generic CE. To illustrate, I would like to be able to write something like this:
But that's not possible ( @wallymathieu not sure if you tried what you suggest, but AFAIK is not possible ). So the only way to define a specialized builder is as @wallymathieu initially suggested:
So, basically what we're doing here is copying the source code from
with the more specific: Finally we remove code that makes no sense in this specific monad, knowing that State is not a monadplus and it's not strict. As you can see this is just boiler-plate. We can define a macro that does all these substitutions (except the code removal). Or you can do it on your and I know some users of this library do this on their side, in order to get less generic code with the advantages it brings (be more specific in the intention and less pressure on/better type inference). The key question here is. Should we include all this boilerplate in this library? I can't find any reason other than "it's boilerplate" not to include it, although for monad transformers it would require further analysis (make the monad a bit less generic? or have a combinatory of all monad combinations?), I did play once with type alias so it's still boilerplate, but it requires only the first 3 lines to be changed, the builder code is exactly the same, still it needs to be repeated. |
Beta Was this translation helpful? Give feedback.
-
I think you are right @gusty that |
Beta Was this translation helpful? Give feedback.
-
Note that for the intention it would suffice to define an alias like
|
Beta Was this translation helpful? Give feedback.
-
Yea, this is exactly my point! 💯
Except that an alias like above will not restrict the types of monad that one can use in the given CE. |
Beta Was this translation helpful? Give feedback.
-
Of course, that's why I said "for the intention", not for the other part. |
Beta Was this translation helpful? Give feedback.
-
Well if you don't restrict to the actual type matching a particular CE, then I'm not too sure this is actually reflecting the intention. It rather sounds confusing and misleading imho. |
Beta Was this translation helpful? Give feedback.
-
@kerry-perret I agree, let's say its name would express the intention but not its type. Another possible advantage of specialized CEs could be Fable 2.0 compatibility. But in order to achieve that, we can't define it by specializing generic code, which Fable won't be able to resolve. We'll have to write the functions by hand for each one, which is even more error prone as here we can't use a macro. |
Beta Was this translation helpful? Give feedback.
-
Tbf, I wasn't expecting to impl. this using a macro. |
Beta Was this translation helpful? Give feedback.
-
OK, let's create a concrete proposal. Some questions to answer:
|
Beta Was this translation helpful? Give feedback.
-
Should we create them using a macro, or by specifying function by function?Might require further investigations. Reason is because of Fable 2.0 compatibility (I'm not an expert, but I'm just suspecting that the F# lang. support is not exactly 100% the same), not sure the macro would just do, I'd say that extra work would avoid to bump into unexpected behaviors. Also, I don't know if majority of people who use Fable tend to use the latest versions (looking at Fable 3.0), which is something that could change the direction we might be hinting to. I guess the benefit of leveraging macro is that it would save us some work. Are we going to specialize everything? What about monad transformers?Same rationale as above, so yea imho. Do these specialized CEs follow the same rules as its generic counterpart?For the sake of consistency, I'd say they should require the All monads are either lazy or strict, but they could be Fx and Plus at the same time. Should we create 2 CE's for those ones?I'm not sure to get this right:
I would like to point out that the issue was initially just about the State Builder, not sure this requires another more general or broader issue. Please, feel free to correct me if I'm wrong (I'm pretty sure there are things I haven't got them right). |
Beta Was this translation helpful? Give feedback.
-
I know the issue you raised was about State only, but if we do something with State we should do the same with Reader and others, that's why I expanded the issue to other types. Regarding macros, we can skip them for now, at least until Fable 3 is around with proper overload support. Monad transformers: if we decide to specialize them, further questions arise. Should we just make them generic (instead of bi-generic) or should we specialize the whole combination?
Which ones? For fx and plus monads how do we name them? We can name |
Beta Was this translation helpful? Give feedback.
-
let asyncMonad: Async<int32> = monad { 42 }
let asyncStrictMonad: Async<int32> = monad' { 42 }
// You can't just type that, you need the return keyword
// the type 'int32' doesn't match the type 'unit'
// async { 42 }
let asyncVanilla: Async<int32> = async { return 42 }
[<EntryPoint>]
let main _ =
asyncMonad |> Async.RunSynchronously |> printfn "%A"
asyncStrictMonad |> Async.RunSynchronously |> printfn "%A"
asyncVanilla |> Async.RunSynchronously |> printfn "%A" Output: 42
42
42
Not sure about MTs, discarding them for the time being, for starters:
Lgtm, might need others feedback |
Beta Was this translation helpful? Give feedback.
-
I was looking for a dedicated computation expression for
State<'s, 'a>
and turns out there was nothing available, so I went to see @wallymathieu and he pointed me towards that implementation: https://gist.github.com/jwosty/5338fce8a6691bbd9f6fI was wondering if there was a rationale for not having
StateBuilder
as part of FSharpPlus.Description
I can't use
State.get
orState.put
in a dedicated CE, I have to resort to use themonad
CE.Repro steps
Please provide the steps required to reproduce the problem
Can use that as an example:
https://github.com/fsprojects/FSharpPlus/blob/ca2b38c8a2ad7cc1a400d8e9bb864ce9548761f2/docsrc/content/type-state.fsx
Expected behavior
Could use a
state
builder, and code above should output2
.Actual behavior
There is not
state
builder built-in implementation as part of FSharpPlus, hence the code above actually cannot compileKnown workarounds
Need to use the
monad
or its strict counterpartmonad'
to in order to benefit fromState<'a, 's>
in a computation expression:or to import the aforementioned implementation in your own code, which seems far from being ideal:
Related information
master
(should be renamed tomain
btw, no?)Beta Was this translation helpful? Give feedback.
All reactions