New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Help: relation between Signal and Task? #389

Closed
sid-kap opened this Issue Sep 3, 2015 · 6 comments

Comments

Projects
None yet
4 participants
@sid-kap

sid-kap commented Sep 3, 2015

Hi,

I'm trying to write an Elm program that reads the current time and then sends the current time in a Http request.
I'm a bit confused on how to do this, since Time.every Time.second is a Signal Time, while Http requests operate inside the Task type.
What is the correct way to do this?
I imagine it should be something like Signal.map (\time -> makeHttpRequest time) (Time.every Time.second) it to get a Signal (Task HttpErr String).
(Although I only want this to execute once, not every second...)

@sid-kap

This comment has been minimized.

Show comment
Hide comment
@sid-kap

sid-kap Sep 3, 2015

It looks like getCurrentTime at http://package.elm-lang.org/packages/evancz/task-tutorial/1.0.2/TaskTutorial#getCurrentTime does exactly what I want.

However, this uses a native function, so it's probably cheating.

sid-kap commented Sep 3, 2015

It looks like getCurrentTime at http://package.elm-lang.org/packages/evancz/task-tutorial/1.0.2/TaskTutorial#getCurrentTime does exactly what I want.

However, this uses a native function, so it's probably cheating.

@rgrempel

This comment has been minimized.

Show comment
Hide comment
@rgrempel

rgrempel Sep 3, 2015

Contributor

No, that's not cheating at all ... in fact, you can't get the time without a native function!

You might find https://github.com/rgrempel/elm-http-decorators interesting -- I also wanted to get the current time and then use it in an HTTP request, as a kind of cache buster.

Contributor

rgrempel commented Sep 3, 2015

No, that's not cheating at all ... in fact, you can't get the time without a native function!

You might find https://github.com/rgrempel/elm-http-decorators interesting -- I also wanted to get the current time and then use it in an HTTP request, as a kind of cache buster.

@sid-kap

This comment has been minimized.

Show comment
Hide comment
@sid-kap

sid-kap Sep 3, 2015

Thanks! I'll take a look at the decorators library.

So it seems that it's acceptable to perform arbitrary IO in the Task monad? And the customary way of doing this is by implementing a native function that returns a Task?

Also, are there any other types in which Elm programmers perform IO actions? Signal?

sid-kap commented Sep 3, 2015

Thanks! I'll take a look at the decorators library.

So it seems that it's acceptable to perform arbitrary IO in the Task monad? And the customary way of doing this is by implementing a native function that returns a Task?

Also, are there any other types in which Elm programmers perform IO actions? Signal?

@rgrempel

This comment has been minimized.

Show comment
Hide comment
@rgrempel

rgrempel Sep 3, 2015

Contributor

I don't have enough Haskell background to be confident that I've understood exactly what you're asking, but here's a stab at it.

It seems to me that, ultimately, a Task is likely to depend on a native function eventually. Now, most of the Tasks that the average Elm programmer creates are merely manipulating other Tasks, as provided by various libraries (either in the core or not). But, if you want to interact with the outside world, eventually you (or the library you are using) are going to need a native function.

Signals also interact with the outside world, but essentially are for cases where there is something outside of your program's control to listen to. For instance, the movement of the mouse can be the basis for a Signal, since it just happens in the outside world and can be communicated into Elm as a Signal. Whereas an HTTP request is more of a one-off request/response cycle, so it fits better with the Task type.

The other way you can do IO is via the port syntax, which I actually don't know that well, since I don't use it much. But it allows you to create a Signal that can be triggered via external Javascript, or can be listened to via external Javascript. And, of course, the external Javascript can do whatever it likes. So, that would be another way in which Signals can be used to do IO.

I hope that helps -- but as I say, my Haskell background is weak, so I may not have understood the question.

Contributor

rgrempel commented Sep 3, 2015

I don't have enough Haskell background to be confident that I've understood exactly what you're asking, but here's a stab at it.

It seems to me that, ultimately, a Task is likely to depend on a native function eventually. Now, most of the Tasks that the average Elm programmer creates are merely manipulating other Tasks, as provided by various libraries (either in the core or not). But, if you want to interact with the outside world, eventually you (or the library you are using) are going to need a native function.

Signals also interact with the outside world, but essentially are for cases where there is something outside of your program's control to listen to. For instance, the movement of the mouse can be the basis for a Signal, since it just happens in the outside world and can be communicated into Elm as a Signal. Whereas an HTTP request is more of a one-off request/response cycle, so it fits better with the Task type.

The other way you can do IO is via the port syntax, which I actually don't know that well, since I don't use it much. But it allows you to create a Signal that can be triggered via external Javascript, or can be listened to via external Javascript. And, of course, the external Javascript can do whatever it likes. So, that would be another way in which Signals can be used to do IO.

I hope that helps -- but as I say, my Haskell background is weak, so I may not have understood the question.

@vilterp

This comment has been minimized.

Show comment
Hide comment
@vilterp

vilterp Sep 3, 2015

Note: Next time please ask this on the elm-discuss google group, since it's a question, not an issue (I don't have the permissions to close)

To take another stab at answering this:

"So it seems that it's acceptable to perform arbitrary IO in the Task monad? And the customary way of doing this is by implementing a native function that returns a Task?"

Yes, this is correct. (we don't usually call Task a monad, but that's pretty much what it is.) Also yes, Tasks and Signals are the only sources of values from the outside world (i.e. not from a pure computation). Ports are signals, so that dichotomy still holds. Signals represent values that change over time (so things like Keyboard.keysDown, Mouse.position and Time.every, etc fit well here), whereas Tasks represent the result of an effectful computation, which is why Http uses them. Mailboxes (in the Signal module) allow you to push new values into signals. Since this is effectful, the function which does it, Signal.send, returns a Task x ().

The format for writing native functions is about to change though (and become way easier/better!), so hold off if you were about to write one. See this proposal: https://gist.github.com/JoeyEremondi/aa87d1ce86a3e5b2944c

vilterp commented Sep 3, 2015

Note: Next time please ask this on the elm-discuss google group, since it's a question, not an issue (I don't have the permissions to close)

To take another stab at answering this:

"So it seems that it's acceptable to perform arbitrary IO in the Task monad? And the customary way of doing this is by implementing a native function that returns a Task?"

Yes, this is correct. (we don't usually call Task a monad, but that's pretty much what it is.) Also yes, Tasks and Signals are the only sources of values from the outside world (i.e. not from a pure computation). Ports are signals, so that dichotomy still holds. Signals represent values that change over time (so things like Keyboard.keysDown, Mouse.position and Time.every, etc fit well here), whereas Tasks represent the result of an effectful computation, which is why Http uses them. Mailboxes (in the Signal module) allow you to push new values into signals. Since this is effectful, the function which does it, Signal.send, returns a Task x ().

The format for writing native functions is about to change though (and become way easier/better!), so hold off if you were about to write one. See this proposal: https://gist.github.com/JoeyEremondi/aa87d1ce86a3e5b2944c

@evancz

This comment has been minimized.

Show comment
Hide comment
@evancz

evancz Sep 3, 2015

Member

I'm going to try to improve the learning resources on tasks when I get the chance, and we have full docs for the Task library ready, I am going to back-port them to the package website in the next few days. That said, Pete has explained things pretty well. Signals let you passively learn about the world. Think of them as a messaging system. Messages go in, messages come out. Tasks let you describe arbitrarily complex interactions that have effects, so they are more about doing stuff once you learn about it through signals. You can send them out ports, and they are run by the runtime system. So if you want to be doing a ton of console stuff, you will use an outgoing port + tasks to describe things. The task API is quite young so a lot of these lower-level APIs are not fleshed out yet, and as they are, I hope people will build APIs on top such that folks can work with data instead of writing tasks themselves.

On the monad thing, it is actually false to say "Task is a monad". That is like saying "Integer is a group". It is true that {Integer,+,0} forms a group in the same way that {Task,andThen,succeed} forms a monad. Point is, part of the reason we don't go for these terms immediately is how imprecisely these terms are thrown around in Haskell. I think a large part of the confusion comes from using these terms like nouns (e.g. "use the state monad" is roughly the same as saying "use the integer group" which is less precise and more complex way of saying "use addition")

I'm gonna close assuming things are sorted out. Please continue here or on the mailing list if not. Again, I will be trying to improve learning resources when I can!

Member

evancz commented Sep 3, 2015

I'm going to try to improve the learning resources on tasks when I get the chance, and we have full docs for the Task library ready, I am going to back-port them to the package website in the next few days. That said, Pete has explained things pretty well. Signals let you passively learn about the world. Think of them as a messaging system. Messages go in, messages come out. Tasks let you describe arbitrarily complex interactions that have effects, so they are more about doing stuff once you learn about it through signals. You can send them out ports, and they are run by the runtime system. So if you want to be doing a ton of console stuff, you will use an outgoing port + tasks to describe things. The task API is quite young so a lot of these lower-level APIs are not fleshed out yet, and as they are, I hope people will build APIs on top such that folks can work with data instead of writing tasks themselves.

On the monad thing, it is actually false to say "Task is a monad". That is like saying "Integer is a group". It is true that {Integer,+,0} forms a group in the same way that {Task,andThen,succeed} forms a monad. Point is, part of the reason we don't go for these terms immediately is how imprecisely these terms are thrown around in Haskell. I think a large part of the confusion comes from using these terms like nouns (e.g. "use the state monad" is roughly the same as saying "use the integer group" which is less precise and more complex way of saying "use addition")

I'm gonna close assuming things are sorted out. Please continue here or on the mailing list if not. Again, I will be trying to improve learning resources when I can!

@evancz evancz closed this Sep 3, 2015

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment