I think this would be a very useful feature!
I forked the Elm compiler and made a working proof of 
concept: https://github.com/eirslett/elm-task-port-example
Is this still something people think would be a good idea?


lørdag 13. august 2016 17.31.07 UTC+2 skrev James Wilson følgende:
>
> 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