+1 for Task ports.

On Friday, 14 April 2017 20:32:24 UTC+2, Nicholas Hollon wrote:
>
> The process:
>
> 1. Share your ideas, experience, & code on elm-discuss.
> 2. Accept that you have no direct influence over what Evan works on next.
> 3. Look at the release history <http://elm-lang.org/blog> for Elm. Notice 
> that changes happen slowly. Notice that improvements to the JavaScript 
> interface (ports, tasks, subscriptions) are spaced out by at least a year. 
> Notice that 0.17 has only been out for about a year.
> 4. Remember that things are going to get better! Just because something 
> isn't being worked on right this minute doesn't mean that it isn't going to 
> be improved in the future.
> 6. Do things in life that make you happy. If it upsets you that Elm lacks 
> something you think is super important, maybe take a break and come back 
> later.
>
>
>
> On Friday, April 14, 2017 at 10:24:46 AM UTC-7, Conner Ruhl wrote:
>>
>> What is the process for requesting these sort of features? Is there one?
>>
>> On Saturday, August 13, 2016 at 10:31:07 AM UTC-5, 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.

Reply via email to