9,242 karma · joined November 30, 2008
The selfish people I know of are politicians and online commenters who think they're entitled to the wealth built by other people.
Right, similar to the equivocation around the meaning of earn in this thread. I've started to wonder whether it's possible to push by accepting that framing and then asking for a justification rather than quibbling about what "stealing" is.
HN used to be open minded about people creating wealth. The change is shocking to me, actually.
On the other hand, I don't see any difficulty with implementing a scheduler using Await/Yield. I don't think it needs access to the full continuation.
> effects are handled by handlers
OK, and in the general case a handler allows its body to "perform" an action, and when the action is performed it has the ability to "respond" to it in (in some cases) a very flexible way, running it never, or multiple times, or in a modified environment, or possibly even passing it out of the scope of the handler entirely.
> function signatures describe the effects that are used
Would you say this is not possible in an untyped language then?
> effectful code is written in direct style, not monadic style
I don't understand the distinction here
Oh, I meant what Haskell calls `IO`, which includes the ability to launch threads, use delimited continuation primops, abort the program, communicate with the FFI, and all sorts of other things that I would guess don't have an algebraic presentation.
So maybe an "algebraic effect" is one that's isomorphic to a free monad of a functor that itself is an algebraic data type. That seems to give an unambiguous specification for what it means to handle an effect (a natural transformation) and to take a "free product" of effects (sum the functors).
On the other hand I think it would mean that things like Future and general IO wouldn't be algebraic effects.
> Algebraic effects & handlers use a free monad and an interpreter to separate the syntax of effects from their semantics
I'm pretty sure not everybody who works with algebraic effects would say they have to be based on a free monad, so I'm skeptical how definitive this definition is.
Bringing it back to my original point, I guess I'd say that if you already have function calls, exceptions and threading built in to the language then you don't need perform/resume except in niche cases (multi-shot continuations being the only case I know of, but I don't even know of many applications of those).
OK, but at the very least it has two primitives "perform" and "resume"? And they're supposed to interact in some particular way?
-- ghci> example
-- Hello
-- World
-- Timed out
example = runEff $ \io -> awaitYield (receiver io) sender
receiver ::
(e1 <: es, e2 <: es) =>
IOE e1 ->
Await String e2 ->
Eff es ()
receiver io a = do
r1 <- await a
effIO io (putStrLn r1)
r2 <- await a
effIO io (putStrLn r2)
mr3 <- timeout io 0 (await a)
effIO io $ case mr3 of
Nothing -> putStrLn "Timed out"
Just r3 -> putStrLn r3
sender ::
e1 <: es =>
Yield String e1 ->
Eff es ()
sender y = do
yield y "Hello"
yield y "World"
yield y "More"
timeout ::
e1 <: es =>
IOE e1 ->
Int ->
Eff es r ->
Eff es (Maybe r)
timeout io t m = withEffToIO
(\effToIO -> System.Timeout.timeout t (effToIO (\_ -> useImpl m)))
ioThanks, that clarifies where you're coming from. Is it possible to specify this protocol somehow, by defining an interface for it? Or by extending lambda calculus with the bits it needs?
(Maybe that's what the Koka folks do in their papers, and if so feel free to say, "yeah read their papers").
What do you think algebraic effects are, if they're not continuations?
EDIT: Ah, based on your comment at https://news.ycombinator.com/item?id=48334737 you might say they're a feature of an intermediate language? So you might take a surface language and "compile to an intermediate language of lambda calculus + algebraic effects", without specifying how that intermediate language is implemented (because it may not even be implemented, per se).
You might like my capability-based effect system for Haskell, Bluefin[1], then. If a Bluefin effectful function throws you can see it in the type system. If you want to have the capability to throw, you need to pass in an argument of type Throw. For example here "workWithThrow" can only throw an exception because it is passed the Throw capability.
workWithThrow ::
(e1 :> es) =>
Throw String e1 ->
Int ->
Int ->
Eff es Int
workWithThrow t x y = do
let result = x + y
when (result > 10) $ do
throw t "Too big"
pure result
-- ghci> example
-- Left "Too big"
example :: Either String Int
example = runPureEff $ try $ \t -> do
workWithThrow t 5 7
[1] https://hackage.haskell.org/package/bluefin type Log = Yield String
-- workWithLogging cannot do arbitrary IO!
-- All it can do is yield log messages, which
-- must be processed elsewhere.
workWithLogging ::
(e1 :> es) =>
Log e1 ->
Int ->
Int ->
Eff es Int
workWithLogging l x y = do
yield l ("x was " <> show x)
yield l ("y was " <> show y)
let result = x + y
yield l ("result was " <> show result)
pure result
-- ghci> example
-- x was 5
-- y was 7
-- result was 12
-- 12
example :: IO Int
example = runEff $ \io -> do
-- forEach determines how each log message
-- should be handled.
forEach
(\l-> workWithLogging l 5 7)
(\logMsg -> effIO io (putStrLn logMsg))> A real effect system allows you to do things like NOT continue execution after using the effect
Right, Bluefin's Request allows you to do that too. For example here is an example of handling the request by continuing or not, depending on what the value yielded to the Request is.
example :: Either String ()
example = runPureEff $ try $ \ex -> do
forEach
( \r -> do
request r True
request r True
request r False
request r True
request r True
)
( \case
True -> pure ()
False -> throw ex "Stopped"
)
> if you "implement" this by using Exceptions, you're not using effects at all, just using Exceptions with extra stepsNot sure I follow that. Above you can see I used an exception (Bluefin's Throw capability), but I couldn't have used only an exception because that would have aborted unconditionally. What am I missing here, that makes "using Exceptions" "not using effects at all"?
> only continuing it after some asynchronous work happens (the Future effect)
I'm not really sure what "a Future effect" is, but I don't see how it's not something that can be run as a function call, at least in Haskell.
> or even "continue" execution several times
Right, these are the multishot continuations which Bluefin doesn't support. I haven't discovered many particularly compelling use cases for multishot continuations but would be very interested in finding some. The developer of the Kyo effect system for Scala, Flavio Brasil, suggested parsing, with multiple parse results, which makes sense.
I'm also not entirely sure Bluefin couldn't simulate common use cases of multishot continuations with threads, but I haven't thought about it very hard.
> You still don't seem to have understood effects.
Possibly true, and part of my puzzlement! I'm always happy to try to improve my understanding. Can you help me see what I've missed?