How do you handle logging then? If f() calls g(), how can I add logging to g() without having to change or recompile f() (and everything in the call stack above it)? ‘You can’t’ is not an acceptable answer.
Not sure why people are saying "you can't" when it seems to me the whole point of algebraic effects that you can. You can define g so that it has no ability to do "general IO", all it can do is yield log messages. Then f can call g in a way that turns the log messages into writes to stdout. For example, here's how you would do it in 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))
If `left_pad()` calls `send_env_vars()`, how can you add exfiltration to `send_env_vars()` without having to change `left_pad()` to expose the use of the network?
This feels like a restatement of the trivially obvious observation that, if your type system is Turing complete, you're going to run into the halting problem.
I don't think that's quite it. In many statically-typed languages that we would typically refer to as having "Turing-complete type systems", types cannot be manipulated at runtime, and thus Type is not really a type in the same way that e.g. int or float are types.
It's sort of like having two languages in one. There is a first, interpreted language which manipulates Types and code and produces a program, and then a second language which is the program itself that ultimately gets typechecked and compiled. Typechecking in this case is not (necessarily) undecidable.
This paper is moreso about dependently-typed languages, where the type of one term can depend on the runtime value of another term.
I'm not sure it is exactly the same. But even if so, someone needed to do the work to prove it. It's also worth noting that proving the undecidability of the halting problem is one of the reasons Turing is so celebrated in the first place.
Because making every jet engine in two different models would make them a lot more expensive. It would also cause maintenance issues because of non-interchangeable parts.
I like this and would seriously consider buying it - except that the purchase page only has prices in something called "AA$". I have no idea what currency that refers to (AA is Aruba, but they don't use dollars), or how much that might come to in my local currency.
Must be a placeholder?
But you briefly made me think it was some sort of Apple-specific currency.
We really aren’t that far away from “Apple Bucks” are we?
I'm in Australia, you're Swedish (but I don't know if your business is based there), and most web sites show US prices by default. So I wouldn't like to guess whether it was meant to be AUD, USD, or SEK.
"The `await` keyword means "turn the rest of this function into a callback for the when the Task I'm waiting on finishes, and return the resulting Task"."
Oh my god thank you. I've been trying to wrap my head around the whole async/await paradigm for years, basically writing code based on a few black magic rules that I only half understand, and you finally made it all clear in once sentence. Why all those other attempts to explain async/await don't just say this I can't imagine.
> Why all those other attempts to explain async/await don't just say this I can't imagine.
On one hand, the people who write explanations are usually those who have lived through a language's history and learned things gradually, and also have context of how things were solved (or not) before a feature was introduced.
On the other hand, the people who are looking for an explanation, are usually just jumping directly to the latest version of the language, and lack a lot of that context.
Then those who do the explaining underestimate how much their explanations are relying on that prior context.
That's why I think the ideal person to explain something to you, is someone with an experience as similar as possible to your own, but also knows about the thing that you want to know about. Because this way they will use terms similar to those you would use yourself. Even if those terms are imprecise or technically incorrect, they would still help you in actually understanding, because those explanations would actually be building on top of your current knowledge, or close to it.
This is also why all monad explanations are... uh... like that.
It's because implementing this is not that easy: there are differences between the implementation of coroutines and await that makes it tricky (especially waiting for both CPU tasks and network events).
How do you handle logging then? If f() calls g(), how can I add logging to g() without having to change or recompile f() (and everything in the call stack above it)? ‘You can’t’ is not an acceptable answer.
reply