Giter VIP home page Giter VIP logo

Comments (16)

rpominov avatar rpominov commented on May 22, 2024 5

Curious what if we just turn derivations into laws? E.g. add a second law for Chain:

u.chain(f => v.map(f)) is equivalent to v.ap(u)

from fantasy-land.

jdegoes avatar jdegoes commented on May 22, 2024 4

Here's an example:

foo :: forall m. (Applicative m) => m Unit
foo = void $ doA *> doB
foo :: forall m. (Monad m) => m Unit
foo = void $ doA *> doB

One intuitively expects the observable behavior of these two functions to be the same. But that's not what happens if monad is inconsistent with applicative.

By the way, there are two ways to solve your problem: simply add a new type wrapper which is ONLY applicative, and not monadic; OR, add a new function to an applicative called zip (or par), which is similar to ap, but which does not promise consistency with monad.

from fantasy-land.

rpominov avatar rpominov commented on May 22, 2024

This actually stated in Fantasy Land but a bit indirectly see rpominov/fun-task#28

from fantasy-land.

safareli avatar safareli commented on May 22, 2024

I think that's a good Idea! and maybe also add them to laws/*.js

from fantasy-land.

SimonRichardson avatar SimonRichardson commented on May 22, 2024

Good spot, there was a PR open in fantasy-promises that did talk about this, but that was probably the wrong place to do so.

from fantasy-land.

safareli avatar safareli commented on May 22, 2024

I have checked PRs and Issues of fl-promises but could not find anything related to it.

One thing to note is that for example Haxl is not fully lawful. I don't know if it's common practice in haskell to partially obey this law.

Some libs might ignore this law but I think we should make it clear that it's also a law and should be obeyed as others.

Currently this are derivations separately from algebras but i think we can move them to algebras like this:

  • map may be derived from bimap => BiFunctor
  • map may be derived from promap => ProFunctor
  • map may be derived from traverse => Traversable
  • reduce may be derived from traverse => Traversable
  • map may be derived from ap and of => Applicative
  • map may be derived from chain and of => Monad
  • ap may be derived from chain => Chain

I would try to create PR to address this issue.

from fantasy-land.

rpominov avatar rpominov commented on May 22, 2024

If we convert derivations to laws, I wonder should we do this for all derivations? Maybe some of them shouldn't actually be laws, maybe in Haskell they doesn't correspond to a law etc.?

Btw, was very interesting to know that in Haskell it also not always obeyed, thank you for the link!

from fantasy-land.

safareli avatar safareli commented on May 22, 2024

@rpominov good point! We should check if all derivations are actually laws in haskell.

from fantasy-land.

safareli avatar safareli commented on May 22, 2024

I was thinking recently that having law that ap must be derived from chain is essential in haskell because for example:

do 
  action1;
  action2;

is translated to action1 >> action2, and as (>>) = (*>) it's action1 *> action2 which is action1 *> action2 = (id <$ action1) <*> action2 and as applicative instance is used in do, it's essential to preserve sequential nature of monad in applicative instance.

But we don't have do notation in js. also it's it's much practical to not enforce sequentiality for example with Task/Future, and also even haxl is ignoring this law and if you want to sequence actions with haxl you should write: (so that do uses monadic bind)

 do 
  _ <- action1;
  action2;

With that in mind I don't see any reason to make applicative sequential when it can be parallel and more practical. So i think this derivation should not be a law, or should be ignored form in Task/Future. What others think on this?

from fantasy-land.

jdegoes avatar jdegoes commented on May 22, 2024

Without the law, it basically means that a program's behavior may change if you change a constraint from Monad to Applicative, or visa versa, even assuming it only uses Applicative functions. More relevant in statically typed programming languages, but still rather unexpected.

Note that a notion of sequentiality is often present for purely Applicative structures, such as Applicative parsing combinators, in which the "left" is parsed before the "right".

Perhaps someone has done work on determining the implications of changing these laws?

from fantasy-land.

safareli avatar safareli commented on May 22, 2024

actually I was not correct in sequentiality

With that in mind I don't see any reason to make applicative sequential when it can be parallel

Applicative should still be sequential, from left to right or right to left. but in case of Task/Future, it should not wait for first task to be finished in order to fork second (Applicative instance derived from chain forks first one and waits until it's finished and then forks second and so on).

In case of maybe, either and other non lazy monadic structures, if we implement ap by hand it's result should be same as result of derived one, even for IO (as it's synchronous). But when we have async structures like Task/Future than the law is not that useful, as it limits us in making ap more efficient (using concurrency).

Perhaps someone has done work on determining the implications of changing these laws?

I might misunderstood your question but currently only fun-task, fantasy-promise and task in new folktale are lawful.

Without the law, it basically means that a program's behavior may change if you change a constraint from Monad to Applicative, or visa versa, even assuming it only uses Applicative functions

Did not quite understand how program's behavior could change, can you provide example or something.

from fantasy-land.

safareli avatar safareli commented on May 22, 2024

I have just read this redit thread on Applicative Effects in Free Monads and there was interesting discussion about the law.

So as this question is already discussed, I will close this issue.
I have created #188 on moving some derivations into corresponding algebras.

from fantasy-land.

masaeedu avatar masaeedu commented on May 22, 2024

@safareli Could you please explain what the conclusion is in general? Is it the case that for any lawful applicative, ap must somehow embed some notion of sequencing as chain does, or is this just a historical artifact of Haskell? As an example, say I have two IOs:

  1. io1 :: IO (String -> String): Sequentially, wait five seconds, raise an exception, return x -> x ++ "bar"
  2. io2 :: IO (String): Sequentially, wait two seconds, delete foo.txt, return "foo"

Let's say I have main = io1 <*> io2. I understand that the value level manipulation (i.e. the application of x -> x + "bar" to "foo") has to proceed in a particular direction and produce "foobar".

What I'm trying to understand is whether the "contextual" effects must also be sequenced. Is it required of a lawful implementation of <*> for IO that foo.txt will not be deleted if the first IO throws, as would be the case with io1 >>= (\f -> io2 >>= (\x -> return f x))? If IO did not throw an exception, would the operation complete in 5 seconds or 7?

from fantasy-land.

safareli avatar safareli commented on May 22, 2024

If a structure is a Monad then it's Applicative instance should behave like ap:

ap f a = do
 f' <- f
 f' <$> a

This rule makes it possible to refactor code using do notation into code using <*>, without it, you would need to think about all use cases this code be used.

In Purescript and in Haskell there is sequential Io/Aff and parallel version Async/ParAff which is not a Monad. (same for with fluture.js too).

But for example Haxl is a lib used for fetching data and has caching built in, so for it all actions could be parallelized as they would have no observable difference (except the speed). tho doing this for IO will definitely have observable difference.

from fantasy-land.

masaeedu avatar masaeedu commented on May 22, 2024

@safareli Thanks. I'm assuming the point of this rule is that the user is forced to convert between different data types and explicitly choose between the two possible (legal) applicative instances, i.e. parallel vs "early exit".

from fantasy-land.

safareli avatar safareli commented on May 22, 2024

yes, so you will convert your sequential values to parallel one, work with it and then covert back.

from fantasy-land.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.