Stream: ideas

Topic: effects in packages


view this post on Zulip Richard Feldman (Jan 01 2022 at 02:26):

I wrote up an idea for how effects in packages could work - any thoughts on it are welcome! https://docs.google.com/document/d/1QAX7lvCFXhtb6h_gHQPBi9ZzqhIt_HEh4fULAwJXQDw/edit?usp=sharing

view this post on Zulip jan kili (Jan 01 2022 at 04:14):

Regarding ecosystem design:
Having the published non-platform packages be mostly/all platform-agnostic would be phenomenal! Enforcing it to be "all" sounds like a good/necessary approach to achieve that. I'm trying to think of a way to eliminate your stated drawbacks (particularly the package author's burden of agnostic-izing the code), but perhaps they're simply part of a subjective tradeoff.

view this post on Zulip Martin Stewart (Jan 01 2022 at 11:59):

Richard Feldman said:

I wrote up an idea for how effects in packages could work - any thoughts on it are welcome! https://docs.google.com/document/d/1QAX7lvCFXhtb6h_gHQPBi9ZzqhIt_HEh4fULAwJXQDw/edit?usp=sharing

I like the idea with passing all the effectful functions in as parameters though until I play around with it I'm not sure if it will be too inconvenient or if that won't be a problem (or at least not enough of a problem to offset the huge gain in simplicity).

One question I have: In your bugsnag example, suppose I wanted something like this?

    {
        error : (BugSnagResponse -> msg), Str, Str -> output msg,
        warning : (BugSnagResponse -> msg), Str, Str -> output msg,
        info : (BugSnagResponse -> msg), Str, Str -> output msg,
    }

That is to say, I want to handle the http response from the bugsnag service. I'm pretty sure my example above won't work because all the fields are stuck using the same concrete msg type and also I'm guessing Roc's type system won't allow output msg. How would this be done instead?

view this post on Zulip jan kili (Jan 01 2022 at 12:39):

@Martin Stewart I believe output and msg are both just type variables (placeholders for whatever you want), so these are interchangeable:

(BugSnagResponse -> output), Str, Str -> output
(BugSnagResponse -> msg), Str, Str -> msg

What do you want msg to look like in practice?

view this post on Zulip Brendan Hansknecht (Jan 01 2022 at 12:50):

The other possibility if required is another layer of transformations. Just like we have the output of a list of bytes as an http request, we would make a response a list of bytes and you would need a function to turn that into a bug snag message

view this post on Zulip jan kili (Jan 01 2022 at 13:00):

@Brendan Hansknecht Like this?

result = await bugsnag.info "foo" "bar"
when result is
    Err e -> ...
    Ok response ->
        message = Bugsnag.parse response
        ...

(Edit: bugsnag.parse --> Bugsnag.parse)

view this post on Zulip Brendan Hansknecht (Jan 01 2022 at 13:09):

I am more specifically talking about this record:

bugsnag : Bugsnag (Task {} [])
bugsnag =
    initBugsnag
        {
            apiKey: "abcdef1234....",
            appVersion: "1.0.0",
            releaseStage: "test",
            user: Guest,
            toOutput: \bytes ->
                Http.sendBytes Bugsnag.url bytes
        }

Add:

fromResult: \bytes -> bugsnag.parse bytes

view this post on Zulip Brendan Hansknecht (Jan 01 2022 at 13:10):

Though for something with http requests and response, i would suggest using a richer type than list u8

view this post on Zulip Brendan Hansknecht (Jan 01 2022 at 13:10):

Probably some basic http types that are easy to transform to/from

view this post on Zulip jan kili (Jan 01 2022 at 13:14):

@Brendan Hansknecht Do you imagine that fromResult would automatically transform the Ok payload in the (expected) Result output of toOutput? Or would it just be manually callable by an application?

view this post on Zulip Brendan Hansknecht (Jan 01 2022 at 13:16):

I assume it would be automatically called in other bugsnag related functions, so mostly hidden from the user of the library. I also assume it would handle both the success and failure case.

view this post on Zulip jan kili (Jan 01 2022 at 13:19):

By expecting output == Result, wouldn't fromResult fail to handle any user applications that use an alternative output type like List Result or something custom?

view this post on Zulip jan kili (Jan 01 2022 at 13:20):

(sorry if this is an unproductive tangent, btw)

view this post on Zulip Brendan Hansknecht (Jan 01 2022 at 13:23):

I'm not sure I understand the question. The process i see is:
1) rich bugsnag type
2) bytes (or generic http request type)
3) application code that creates bugsnag request and creates tasks or similar, might even group requests
4) platform sends http request
5) platform receives http response
6) application lambda to process results
7) bytes (or generic http response type)
8) rich bugsnag type (including potential failure)

view this post on Zulip jan kili (Jan 01 2022 at 13:33):

In your example above, toOutput: a -> b and fromResult: a -> c. How could a platform-agnostic Bugsnag package automatically infer the transformation from b to a in order to chain toOutput with fromResult?

view this post on Zulip Brendan Hansknecht (Jan 01 2022 at 13:35):

Sorry, yeah, those can't be chained. They depend on platform api

view this post on Zulip jan kili (Jan 01 2022 at 13:40):

Luckily, if a user wanted to ignore all errors (or whatever other strangeness their toOutput might return), toOutput could simply contain the response parsing step. Essentially, if fromResult's input type were to match toOutput's output type, then fromResult's code could just be injected into the end of toOutput.

view this post on Zulip Brendan Hansknecht (Jan 01 2022 at 13:57):

Very true

view this post on Zulip Brendan Hansknecht (Jan 01 2022 at 13:57):

Then it doesn't need to even use a type variable at all. Just specify its expected types.

view this post on Zulip Martin Stewart (Jan 01 2022 at 14:34):

JanCVanB said:

Martin Stewart I believe output and msg are both just type variables (placeholders for whatever you want), so these are interchangeable:

(BugSnagResponse -> output), Str, Str -> output
(BugSnagResponse -> msg), Str, Str -> msg

What do you want msg to look like in practice?

Sorry I wasn't very clear. I'm thinking in terms of the Elm Architecture where making an http request also means including a parameter (the BugSnagResponse -> msg part) that says how the http response can be converted into a value that gets passed to an update function. This is to say, the update function isn't called via Task.await. Instead the platform calls update after the http request is completed.

view this post on Zulip jan kili (Jan 01 2022 at 15:12):

Hmm, I don't know Elm so I don't have a feel for that pattern, but... (a) you can customize what the toOutput function returns, (b) I imagine that whatever BugSnagResponse -> msg transformation you want to chain, you could simply add that as a final step of the toOutput function you provide, and (c) you may want to perform error handling at the same level that calls bugsnag.error/warning/info. Does anyone with Elm experience have better insight here?

view this post on Zulip Lucas Rosa (Jan 01 2022 at 18:06):

I like the approach that limits the package registry because it's easier to add stuff later than it is to take stuff away

view this post on Zulip Lucas Rosa (Jan 01 2022 at 18:07):

so if only allowing packages that are platform agnostic becomes a problem, we can then start the work of enabling platform specific packages, vs. platform specific packages being allowed then becoming a problem it'll be impossible to take it away once it's there without significant outrage

view this post on Zulip Lucas Rosa (Jan 01 2022 at 18:09):

One thing of the top of my mind is the elm native code thing that caused quite a bit of drama. Now I'm not arguing one way or the other in that regard, just bringing up an example of how taking things away that people are used to can be quite upsetting even if it's for the best.

view this post on Zulip Johannes Maas (Jan 01 2022 at 22:37):

I'm wondering about this: Would there be a way to share the platform-specific adapters? It feels like that might be important convenience instead of reimplementing the boilerplate for each usage.

view this post on Zulip Brendan Hansknecht (Jan 02 2022 at 19:41):

Platform specific adaptors have a few options in my view:
1) as part of the platform
2) as a library that shares types with the platform (will have limitations)
3) add some sort of interface spec that enables depending on platform functions without directly depending on the platform
4) special libraries that depend on platforms

view this post on Zulip jan kili (Jan 06 2022 at 09:05):

Based on this paragraph from the README, t seems that offering ecosystems of platform-specific packages was, at one point, seen as a feature of Roc:

Each Roc platform gets its own separate package repository, with packages built on top of the API that platform exposes. This means each platform has its own ecosystem where everything is built on top of the same shared set of platform-specific primitives.

view this post on Zulip jan kili (Jan 06 2022 at 09:07):

I assume that thinking around this changed, but it just stood out to me as relevant here

view this post on Zulip Richard Feldman (Jan 06 2022 at 16:20):

good point! I've updated the README to remove that paragraph :thumbs_up:


Last updated: Jun 16 2026 at 16:19 UTC