Skip to content
This repository was archived by the owner on Oct 4, 2020. It is now read-only.

Use untagged IO as default, with tagged Eff to supplement. #25

Closed
natefaubion opened this issue Jul 3, 2017 · 36 comments
Closed

Use untagged IO as default, with tagged Eff to supplement. #25

natefaubion opened this issue Jul 3, 2017 · 36 comments

Comments

@natefaubion
Copy link

Discuss.

@natefaubion
Copy link
Author

We have an IO type in purescript-io. I think that it's something that should be moved into core, and we should encourage new libraries to write IO FFI imports instead, optionally supplementing them with Eff effect labels.

@paf31
Copy link
Contributor

paf31 commented Jul 3, 2017

See #20, although I think we should do it the other way around as a breaking change.

@jdegoes
Copy link

jdegoes commented Jul 3, 2017

I heartily agree with this idea.

Eff's effects are described by strings (well, symbol/kind pairs), and are not semantic. One string in one library doesn't correspond to another string in another library (or sometimes corresponds to multiple in some chaotic and changing fashion), and there is no way to do algebraic effect introduction and elimination, which are cornerstones of a principled algebraic effect system.

The row types in PureScript have been proven to be extremely useful, but I'd argue the effect system that piggybacks on row types has created enormous work for developers and adds layers of complexity not found in Elm, for no more actual guarantee of compile time benefits.

An IO type would make PureScript dramatically simpler, more accessible, and easier to use, while not sacrificing anything significant (strings in row types are still strings!).

I know Bodil and several others have a similar opinion here.

@garyb
Copy link
Member

garyb commented Sep 22, 2017

Regarding naming, some comments from elsewhere.

@spicydonuts:

I don't know much of the background on this, but Eff e a -> Eff a seems like a much better way to go if everything currently using Eff and Aff should be removing its effect rows. IO seems better if there's still a use case for keeping the current Eff implementation around (no idea what that would mean for Aff). I'd prefer modifying Eff and Aff in that case, because it basically keeps things working the way they are, just easier to work with. Converting to IO seems confusing from a dependency perspective (ex: pulling in 3 libraries and each use a different version of IO/Eff).

me:

I'm slightly more in favour of keeping the Eff name and dropping the row than renaming to IO too. I could get used to it either way, but it definitely seems like the update overhead will be lower by continuing with Eff / Aff.

@hdgarrood
Copy link
Contributor

I personally am not sure that we should prioritise update overhead vs what name would make the most sense, especially since we are still pre-1.0. Given that (afaict) the name Eff comes from the Haskell extensible effects paper, there doesn’t seem to be any reason for the type to retain the name Eff after we have removed the row type parameter. This change is going to break everything using Eff anyway, so why not choose the name that makes the most sense (which is of course IO)?

@garyb
Copy link
Member

garyb commented Sep 22, 2017

The Eff we have is also nothing like the extensible effects paper though, purescript-run is more like that.

The name Eff still makes sense to me as it's "the monad in which effects are performed" regardless of whether its indexed by types-of-effects or not. But yeah, I don't feel that strongly about it.

If we do want to switch to the IO name, I'll see what @jdegoes thinks about donating the purescript-io name for that instead of having it as the current Aff-based IO.

@hdgarrood
Copy link
Contributor

Of course our Eff is quite different to the paper’s, but my impression was always that our Eff was called that because of the characteristic it shares with the paper’s Eff, that there are two type parameters: the first being some kind of set of effects, and the second being the concrete result type. Our current Eff at least has this in common with the paper’s; I think after we remove the row the connection would be much more tenuous.

Generally we’ve taken names from Haskell wherever it made sense too, eg “Prelude”, the Functor hierarchy, <> for the monoid operation, etc, and so I think it would make sense to use the same scheme here.

If the PureScript type had never had a row type parameter, what do you think it would have been called?

@garyb
Copy link
Member

garyb commented Sep 22, 2017

If the PureScript type had never had a row type parameter, what do you think it would have been called?

Fair point!

@jdegoes
Copy link

jdegoes commented Sep 22, 2017

I'm fine donating the name if that's how things go down. The name Eff or Effect isn't that bad, if you consider it from the perspective of Javascript developers exploring PureScript rather than Haskell developers crossing over. But debates over names are not that productive, IMO, so I don't really care one way or another.

@paf31
Copy link
Contributor

paf31 commented Sep 22, 2017

I like the idea of using IO since it's common terminology with Haskell, but I also like the idea of keeping the name Eff. However, I think it might be confusing if people find old documentation referring to Eff talking about rows, which then doesn't make sense any more.

One concern I have about IO is that our IO would not be like the one in Haskell in some sense. IO in Haskell is asynchronous, more like the current purescript-io, but our IO would be synchronous. In that sense, Eff makes more sense to me, and I actually think keeping the current purescript-io as it is makes quite a bit of sense.

I'd also consider some other alternatives. A few ideas: Effect,Sync, Task, Method, Proc, Action. I don't feel particularly enthusiastic about any of them, apart from Effect.

@hdgarrood
Copy link
Contributor

Oh, I was thinking that Haskell's IO is closer to our Eff than our Aff / IO but on second thoughts I suppose that's not really the case at all!

I do think it would be good to choose names for the default types for synchronous and asynchronous native effects so that it's clear that they are closely related. So I suppose if we're leaving IO as it is, that suggests we should use consider IOSync or something like that as a new name for Eff?

Also, does it make sense to have two distinct types for sync and async effects in other backends? Should we maybe be considering alternative backends in this discussion too?

@paf31
Copy link
Contributor

paf31 commented Sep 22, 2017

So I suppose if we're leaving IO as it is, that suggests we should use consider IOSync or something like that as a new name for Eff?

If we were to go that route, I'd like to call it Sync, but I would like to try to make it clear that Eff is the more basic effect monad in some sense, corresponding to the way in which JS code executes.

@hdgarrood
Copy link
Contributor

Wait, sorry, I mean if IO is the default async effect monad of kind Type -> Type, then I'm just saying it would be best to include "IO" somewhere in the name for the default sync effect monad of the same kind. Why would we have both Sync and Eff?

@paf31
Copy link
Contributor

paf31 commented Sep 22, 2017

No, I'm saying I would rather not have IO and IOSync, because that makes it seem like IOSync is a special case of IO, when IO would be built on top of IOSync.

My vote is for renaming Eff :: # Effect -> Type -> Type to Effect :: Type -> Type, and then dealing with Aff and IO separately.

To clarify, I think this is the best option because it a) doesn't lead to confusion with the current Eff, b) doesn't lead to confusion with Haskell's IO, which is asynchronous, and c) is a decent name based on what Eff actually is, i.e. a representation for some side effect to be performed.

@hdgarrood
Copy link
Contributor

Ah, I see. Yes, that sounds good to me, although I think it would be good to have a plan for Aff and IO before we go ahead with this.

@paf31
Copy link
Contributor

paf31 commented Sep 22, 2017

Affect? 😄

Just joking, and in any case I'd like to leave that decision up to the Aff maintainers, and not worry about Aff when choosing the new name for Eff.

@hdgarrood
Copy link
Contributor

I was thinking, I suppose Aff might not need to continue to exist at all?

@paf31
Copy link
Contributor

paf31 commented Sep 22, 2017

How's that?

@paf31
Copy link
Contributor

paf31 commented Sep 22, 2017

Oh, because it would become IO?

@hdgarrood
Copy link
Contributor

Yeah, exactly. It sounds like we aren't intending to continue providing a type of kind # Effect -> Type -> Type for synchronous effects, so in that case why provide an async version of the same thing, especially since IO already exists?

@garyb
Copy link
Member

garyb commented Sep 23, 2017

@hdgarrood I imagine we'll do the same thing with Aff and just drop the effect row. And then rename to Affect 😜

(or AsyncEffect...)

@joneshf
Copy link

joneshf commented Sep 23, 2017

Whatever happens with the non-extensible effects and naming, I don't really care. But, I think dropping the rows loses something.

It was mentioned in slack that Run a b is closer to the spirit of the paper than Eff a b we have now. In an effort to keep Eff a b with extensible effects, could we bring in Run a b and rename it to Eff a b? The non-extensible effects can still be the default, and the name can be whatever. But, the extensible part can also stay extensible (as the non-default).

@hdgarrood
Copy link
Contributor

What does that achieve over leaving Run as is?

@Pauan
Copy link

Pauan commented Sep 23, 2017

My thoughts:

  • I don't care a lot about the names, but:

  • I like the name IO

  • I like the names Sync and Async

  • I don't like the names Eff and Aff

  • I think the name of the sync monad should be related to the name of the async monad (currently they are Eff and Aff, respectively)

  • So my vote is in favor of Sync and Async (or IO and IOAsync, or IOSync and IOAsync)

  • I'd also be fine with getting rid of Eff and just using Aff (renamed to IO) for everything, but that does have a performance penalty

@garyb
Copy link
Member

garyb commented Sep 23, 2017

Another 2p from me: I think SyncIO / AsyncIO reads better and is easier to say aloud than IOSync / IOAsync, if we're considering naming like that.

I'm not really a fan of Sync / Async alone, as that name suggests nothing about effects.

@paf31
Copy link
Contributor

paf31 commented Sep 23, 2017

I'd also be fine with getting rid of Eff and just using Aff (renamed to IO) for everything, but that does have a performance penalty

and more FFI complexity. I think we need Eff for a simple, sensible FFI.

@paf31
Copy link
Contributor

paf31 commented Sep 23, 2017

Also, I think run is an amazing library, but I don't know if it's the best default for the core libraries. The core libraries should contain the simple, low-level abstractions IMO. For the same reason, I like that aff is a separate thing.

@megamaddu
Copy link

megamaddu commented Sep 23, 2017

Maybe not related but I do really like standardizing the sync and async effect FFI. (Sync/Eff as a simple thunk and Async/Aff as (resolve, reject) => { try { resolve(...) } catch (e) { reject(e) } }. Aff currently supports a lot more than that and that's fine, but that simple FFI integration point that doesn't break between Aff releases is important (since it can't be type checked). Maybe that just means EffFFI and AffFFI should just be compiler built-ins and real libraries should build off of runSyncIOFn and runAsyncIOFn (again, just making up names).

@jdegoes
Copy link

jdegoes commented Sep 25, 2017

If Eff e a gets renamed to Eff a, which I have come around to preferring because it suggests the default in libraries should be to drop the effect row, i.e. it's part of migration, or if it gets renamed to Sync a (emphasizing more its synchronous nature rather than its effectful nature), then I think we might swap Aff e a and IO a, i.e. IO becomes the new Aff e a implementation (4.0), while Aff e a becomes a smaller wrapper on top for tracking effects via rows. Existing Aff is a good candidate for the IO name since it's asynchronous and supports the moral equivalent of things like forkIO. But it's too complex / opinionated to be a replacement for Eff (though the current version, note, is quite fast for synchronous code, about as fast as `Eff).

@megamaddu
Copy link

If it's that fast it might be better in the long run to use IO for both, then just convert FFI to IO using specific functions similar to the runEffFnX functions. 🙂

@jdegoes
Copy link

jdegoes commented Sep 26, 2017

If it's that fast it might be better in the long run to use IO for both, then just convert FFI to IO using specific functions similar to the runEffFnX functions.

The main issue with this is that Aff is not guaranteed to be synchronous. It preserves synchronous effects (i.e. does not change them to asynchronous), but Aff a may be some arbitrary combination of synchronous and asynchronous code. Lots of Javascript FFI code requires synchronous execution (e.g. cancel event bubble), so Eff is useful to precisely describe that.

@gabejohnson
Copy link

From https://github.com/purescript/purescript-eff#purescript-eff

The Eff monad, for handling native side effects.

I propose Eff e a -> Native a

@Pauan
Copy link

Pauan commented Oct 9, 2017

@gabejohnson I like it, but what about Aff?

@natefaubion
Copy link
Author

@Pauan Aff doesn't have the same baggage as Eff. I don't think there's any reason to change it.

@urs-of-the-backwoods
Copy link

Just a comment to a gradual change: please make it a breaking change (switch to new Eff without rows) and do not mix in the exisiting one. Might be much harder but keeps the clarity of purescript (and the ease of anticipation), which is what everybody likes on the language.

@garyb
Copy link
Member

garyb commented Apr 7, 2018

Closing this, since https://github.com/purescript/purescript-effect

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants