>
> But isn't that covered by the error handling of tasks? The same way as
> when you use the "built-in" tasks like Http? Http gets around the general
> issue of "what happens if this never returns" using the Http.Error value
> Timeout. Maybe something similar could be used for Task Ports.
It's hard to disagree with that rationale. A "Timeout" and
"UnexpectedValue" error would be all that is needed to handle cases where
functions return no promise, a non-resolving promise, or a promise with an
incorrect return type.
On Tuesday, August 23, 2016 at 6:21:38 PM UTC-4, Tim Stewart wrote:
>
> But isn't that covered by the error handling of tasks? The same way as
> when you use the "built-in" tasks like Http? Http gets around the general
> issue of "what happens if this never returns" using the Http.Error value
> Timeout. Maybe something similar could be used for Task Ports.
>
> In the same case with standard Cmd / Sub, you would just never receive the
> Sub. It gives you LESS opportunity to handle the JS error through a timeout
> or similar mechanism.
>
> Not to mention the added complexity when using Cmd / Sub of tracking
> separate, parallel calls to the same port. You have the overhead not only
> of managing two different entry points to your update function (as with any
> Cmd/Sub pair for an essentially single asynchronous operation), but of
> encapsulating some kind of state identifier that lets you match the
> incoming Subs with whatever Cmd they related to in the first place. The use
> of Tasks on the Elm side and Promises on the JS side makes this unnecessary.
>
> I know, Elm design decisions are based on concrete use cases not on waffly
> hand-waving. I'll try and find time to write one up.
>
>
> On Tuesday, August 23, 2016 at 11:56:06 PM UTC+10, Erik Lott wrote:
>>
>> Although I'd love to be able to use and compose port commands like tasks,
>> I'm not a big fan of this solution.
>>
>> regular ports only let you send data off or receive data back, not both.
>>
>>
>> This is a good design decision. Ports isolate Elm from the underlaying
>> language (javascript) and avoids creating dependencies on the outside
>> implementation: you fire commands out of ports, and don't need to be
>> concerned about what happens to that data once it's exited the port. You
>> receive subscription messages from js, and don't have to be concerned about
>> how they were generated.
>>
>> As soon as you send a command out of a port and expect something in
>> return, you've created a dependency, and a new source of runtime errors
>> that the compiler can't catch.
>>
>>
>> On Saturday, August 13, 2016 at 11:31:07 AM UTC-4, James Wilson wrote:
>>>
>>> The problem
>>>
>>> ports as they stand are fundamentally incompatible with Tasks. Being
>>> backed by Cmd's, they are harder to compose. A frustration of mine is that
>>> often we are directed to "just use ports" when a proper interface to some
>>> native API is not yet available, but this leads to our Msg types growing
>>> and more significant changes being required when eventually the proper
>>> interface is made available.
>>>
>>> Also, many JS interop things I find myself wanting to do are
>>> fundamentally one-shot functions which I expect a result back into Elm from
>>> immediately, or otherwise just want to compose with other Task based
>>> things. Some examples that come to mind of one-shot tasks you may want to
>>> compose rather than use the streaming interface that ports provide:
>>>
>>> - Getting items from local/sessionStorage
>>> - .. really, most things involving working with the Web API that
>>> arent yet implemented in Elm.
>>> - Embedding JS widgets into Elm elements
>>> - Using a JS library for doing things like hashing passwords or
>>> obtaining some data back from some custom service
>>> - Interacting with things like Electron for creating apps that can
>>> run in the desktop and interact with the filesystem etc.
>>>
>>>
>>> The solution
>>>
>>> Task ports. The idea is that these are defined the same way that Ports
>>> in elm currently are, but they return a Task type rather than a Cmd or Sub
>>> type. On the JS Side, we attach a function to the Elm app that returns a
>>> Promise, and on the Elm side we wait for the Promise returned to reject or
>>> resolve, and marhsall the error or result from the promise into the error
>>> or result type required by the Task type of the port.
>>>
>>> Let's see how this might work:
>>>
>>>
>>> *Ports.elm:*
>>>
>>> port apiSession: Task String SessionId
>>>
>>>
>>>
>>> *Main.elm:*
>>>
>>> import Ports
>>> import Json.Decode as Decode
>>> import Task exposing (andThen)
>>>
>>>
>>> -- get an API session from JS land and make an http request using it
>>> -- given some path and a decoder to decipher the result:
>>> apiRequest : String -> Decoder a -> Task ApiError a
>>> apiRequest path decoder =
>>> let
>>> headers sessId =
>>> [ ("Content-Type", "application/json")
>>> , ("MyApp-SessionId", sessId)
>>> ]
>>>
>>>
>>> req sessId = Http.send Http.defaultSettings
>>> { verb = "POST"
>>> , headers = headers sessId
>>> , url = path
>>> }
>>>
>>>
>>> decodeResponse res = Decode.decodeString decoder -- ...handle error
>>> etc
>>> in
>>> Ports.apiSession `andThen` req `andThen` decodeResponse
>>>
>>>
>>> *App.js:*
>>>
>>> Elm.Main.ports.apiSession = function(){
>>> return new Promise(function(resolve,reject){
>>>
>>>
>>> var sess = localStorage.getItem("sessionId");
>>> if(!sess) reject("NO_SESSION");
>>> else resolve(sess);
>>>
>>>
>>> });
>>> }
>>>
>>> var app = Elm.Main.fullscreen();
>>>
>>>
>>>
>>>
>>> Here, we use a tiny bit of JS to access localStorage and pull out a
>>> session ID. This function is used whenever the apiRequest Task is performed
>>> in Elm, and composes nicely into our apiRequest without the need for a
>>> complicated effect manager or threading a sessionId through everywhere just
>>> because we need to get it from a Cmd based port.
>>>
>>> One of the nice things about this is that there is minimal refactoring
>>> to do for those things that do eventually receive coverage in the Elm Web
>>> API - you're just swapping out Tasks for other Tasks. As the Web API will
>>> always be changing, I think that having a nice way to make JS polyfills
>>> like this will always have some value, let alone for interacting with
>>> libraries written in JS that haven't or won't ever be ported to Elm.
>>>
>>> Elm would continue to make the same guarantees as with other ports; if
>>> the task port can't marshall the response back into Elm an error would be
>>> thrown along the same lines as is currently done via ports.
>>>
>>> Summary
>>>
>>> - regular ports only let you send data off or receive data back, not
>>> both.
>>> - Cmd's and Sub's are not composable
>>> - Task based ports allow you to create a new Task that is backed by JS
>>> - Task based ports allow for better composition and less friction when
>>> the backing JS is eventually implemented in Elm.
>>>
>>> I'd love to hear what people think about this. Perhaps I'm missing some
>>> big issues with the idea for instance, or maybe it's an awesome idea :)
>>> What do you all think?
>>>
>>>
--
You received this message because you are subscribed to the Google Groups "Elm
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.