Correct, but Bluefin does more than ST
. One way to see Bluefin is as a "generalized ST
". Itâs generalized because it also treats exceptions and IO
, neither of which ST
treats. Regarding the former, you can write
bar ::
(e1 :> es, e2 :> es) =>
State Int e1 ->
Exception Bool e2 ->
Eff es Foo
result :: Foo
result = runPureEff $
evalState 0 $ \sInt -> do
handle (\case True -> defFoo1; False -> defFoo2) $
\exBool -> do
bar sInt exBool
removing the Exception
from scope, as well as the State
, and ending up with a pure value. Regarding IO
, it cannot be removed from scope, but its absence (i.e. the absence of an IOE e ->
argument) shows that a function does not do IO
.
That pattern is syntactically convenient, and common in the Haskell world (for example, withFile
), but it doesnât actually guarantee removing effects from scope, as you point out.
Unfortunately thatâs not sufficient. Consider the following attempt at scoping a resource:
withResource ::
(forall s. Resource s -> IO r) ->
IO r
useResource :: Resource s -> IO Result
Then I can write
bad :: IO (IO Result)
bad = withResource $ \resource ->
pure (useResource resource))
and get access to the resource outside of the scope of withResource
. Thatâs bad! (@Leary explained this to me in an earlier thread on the topic).
ST
does allow to block effects from escaping, but it is not really simpler: itâs just doing less. You can write almost the example same code in Bluefin as you would in ST
, if all you wanted to do was to manipulate state.
IO
does not allow to block effects from escaping, as demonstrated above.
I feel that need, as do some others. Maybe you and different others do not. Thatâs fair enough. But assuming you do want to remove effects, and you do want value-level effects handles, then I donât think youâll find a simpler API than Bluefinâs. (One way this might change is if Haskell got a type level set type. Then we could simplify the types, for example doing away with the e1 :> es
constraint.)
Yes, but only as long as you didnât want to use exceptions as well as state.
I think itâs worth it. You may not. Itâs probably a matter of preference.
Well, not really, but thatâs a bit like asking âis there any other benefit of pure functional programming, besides all functions returning the same result for the same input?â. In both cases the simple foundation provides something very sturdy to build reliable software on top of!