This is a bit pedantic, but over the course of learning Elm I have 
sometimes found the use of magic symbols a bit confusing. This is the first 
I heard of the ! operator. In 2 of the three places it is used in the code 
[1] the ! infix operator is not really shorthand for the tuple form because 
it amounts to the same number of characters (surrounding a single Cmd with 
square brackets instead of the entire expression with round brackets, and 
using an exclamation mark instead of a comma), and it is only a shorthand 
in the other place because the empty list evaluates to Cmd.none. The tuple 
form would have been more familiar and readable to a relatively 
inexperienced Elm programmer like me - particularly in the place where it 
added most conciseness, as Cmd.none is surely more expressive than [].

Kudos for sharing the code example. I don't mean to be critical, just 
reflective.


[1] https://gist.github.com/pdamoc/d492ab58023926cd4d4950f12e5e170d

On Thursday, June 2, 2016 at 5:42:16 AM UTC+10, Peter Damoc wrote:
>
> I'm guessing you referring to: 
>
> http://package.elm-lang.org/packages/elm-lang/core/4.0.1/Platform-Cmd#!
>
> This is the code : 
>
> (!) : model -> List (Cmd msg) -> (model, Cmd msg)
> (!) model commands =
>   (model, batch commands)
>
>
> I use it as a short hand for joining the model and the Cmds. 
> It's also pretty handy when you don't have a Cmd since `batch []` 
>  evaluates  to Cmd.none. 
>
>
>
>
>
> On Wed, Jun 1, 2016 at 10:18 PM, Simon <[email protected] <javascript:>> 
> wrote:
>
>> This is super cool.
>> But what is the function:
>>
>> (!) : a -> List b -> (a, b)
>>
>> On Wednesday, 1 June 2016 09:45:05 UTC+2, James Wilson wrote:
>>
>> Thanks for your help; I'll have a ponder and probably end up taking an 
>>> effect manager route again (I started this way and then ended up with some 
>>> weird hybrid where my cache was in the main elm app and an effect manager 
>>> did the "lower level" api stuff; but I'm not so happy with it)
>>>
>>> As another effect manager code point if you're intrigued how they work 
>>> (and my comments are in any way useful, which they may not be); here's an 
>>> effect manager I made for sending and receiving things from a backend I'm 
>>> wiring my app up to (so it uses custom Cmds and Subs):
>>>
>>>
>>> https://github.com/jsdw/tl-asset-browser/blob/3e9e8527f65ae0a0a2d5d00d01779bdfdf03701d/src/elm/Api.elm
>>>
>>> Not sure whether it's any help (may just be easier looking at the 
>>> elm-lang effect managers!) but just in case :)
>>>
>>> On Wednesday, 1 June 2016 08:31:11 UTC+1, Peter Damoc wrote:
>>>>
>>>> I haven't used an effect manager for this because I haven put in the 
>>>> time needed to learn how to create effect managers. :) 
>>>>
>>>> If what I've shown here can be accomplished with an effect manager then 
>>>> that's the way it should be done.  :) 
>>>>
>>>>
>>>>
>>>>
>>>>
>>>>
>>>> On Wed, Jun 1, 2016 at 10:21 AM, James Wilson <[email protected]> wrote:
>>>>
>>>>> Thanks, that looks like basically exactly what I'd have guessed :) 
>>>>> It's super useful seeing an actual code sample with these ideas in.
>>>>>
>>>>> One thing I wonder now is; why not use an effect manager for this? It 
>>>>> basically seems to fit the exact same space (allows you to create a 
>>>>> custom 
>>>>> Req like thing that can be mapped and batched and passed up the component 
>>>>> hierarchy - except that it's just a Cmd instead and plays nice with other 
>>>>> Cmds; allows you to maintain and update state (the cache) as you go; 
>>>>> allows 
>>>>> you to "convert" Reqs to tasks to be run - just Cmds again now). In fact, 
>>>>> effect managers don't really seem to help you do anything other than 
>>>>> what's 
>>>>> described here (plus a subscription side if you want it). Are there any 
>>>>> cons to using an effect manager here that you have in mind?
>>>>>
>>>>> On Tuesday, 31 May 2016 20:43:41 UTC+1, Peter Damoc wrote:
>>>>>>
>>>>>> The updating of the cache sounds to me like this: 
>>>>>>
>>>>>> 1. if we have the info in cache, just supply the info without a HTTP 
>>>>>> GET
>>>>>> 2. if we don't have the info in cache, return a different Msg that 
>>>>>> encapsulates the msg that requested the original information and the 
>>>>>> info 
>>>>>> required for the cache update. 
>>>>>>
>>>>>> Here is a quick update of the code I've previously posted to include 
>>>>>> this caching mechanism. 
>>>>>>
>>>>>> https://gist.github.com/pdamoc/d492ab58023926cd4d4950f12e5e170d
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, May 31, 2016 at 10:05 PM, James Wilson <[email protected]> 
>>>>>> wrote:
>>>>>>
>>>>>>> The key part that's not coded in the gist is the use of a 
>>>>>>> cache/global state object, however I think I  see what you're getting 
>>>>>>> at - 
>>>>>>> pass back up the chain a Req object, say, and at the top we can turn it 
>>>>>>> into a Cmd using, say, some top level global state as well as whatever 
>>>>>>> other data we need. This may lead to a request being made to the server 
>>>>>>> or 
>>>>>>> it may not.
>>>>>>>
>>>>>>> The other part of the puzzle is actually updating the cache when a 
>>>>>>> request is made. Req.toCmd for instance could return an updated 
>>>>>>> GlobalState 
>>>>>>> so that it's able to cache "pending" states on values (so that we can 
>>>>>>> avoid 
>>>>>>> duplicating requests). To update the cache when the response actually 
>>>>>>> comes 
>>>>>>> in we could have toCmd return a Cmd.batch of 2 commands, one that will 
>>>>>>> fail/succeed and send a message to the component that initiated the 
>>>>>>> Req, 
>>>>>>> and one that will send a message aimed at the top level cache itself.
>>>>>>>
>>>>>>> Thanks Peter, I'll definitely mull over this!
>>>>>>>
>>>>>>> On Tuesday, 31 May 2016 19:45:42 UTC+1, Peter Damoc wrote:
>>>>>>>>
>>>>>>>> ADT in Elm is one of its most powerful weapons. 
>>>>>>>>
>>>>>>>> You could encapsulate your requests in a type and use this type at 
>>>>>>>> top level to fulfill them. 
>>>>>>>>
>>>>>>>> For example: instead of returning Cmd msg you return some Req msg 
>>>>>>>> that can be turned into a Cmd msg at top level based on some context 
>>>>>>>> information. 
>>>>>>>>
>>>>>>>> Here is a gist with a skeleton of how I view this implemented:
>>>>>>>> https://gist.github.com/pdamoc/a47090e69b75433efa60fe4f70e6a06a
>>>>>>>>
>>>>>>>> I've sent the base of the URL as a simple String in `Req.toCmd` but 
>>>>>>>> you can imagine a more complex type holding all kind of information 
>>>>>>>> (e.g. 
>>>>>>>> cache, auth, etc ) . 
>>>>>>>> Also, I've kept the type of the Req simple (only saved the rest of 
>>>>>>>> the URL based on the user and the request) but one could use it to 
>>>>>>>> store 
>>>>>>>> all the info needed when you will turn the Req into a Cmd. 
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, May 31, 2016 at 7:29 PM, James Wilson <[email protected]> 
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>> In Elm, each component basically has its own internal state (which 
>>>>>>>>> is actually all just a slice of one global model). In my app, I also 
>>>>>>>>> want 
>>>>>>>>> global state that is independant of any components; for example a 
>>>>>>>>> clientside cache of various API responses (asset details - there 
>>>>>>>>> could be 
>>>>>>>>> many thousands, user authentication status).
>>>>>>>>>
>>>>>>>>> I want any component to be able to call methods that make use of 
>>>>>>>>> this global state. For example, a method to obtain details for items 
>>>>>>>>> in the 
>>>>>>>>> current view might first look at the global state to see if these 
>>>>>>>>> items are 
>>>>>>>>> cached. If they arent, the call would provide a Cmd to be issued that 
>>>>>>>>> gets 
>>>>>>>>> the items (and puts them in the cache), while simultaneously updating 
>>>>>>>>> the 
>>>>>>>>> state to indicate that they are being loaded (so that the same 
>>>>>>>>> request 
>>>>>>>>> again from another component doesnt trigger another call to the 
>>>>>>>>> backend). 
>>>>>>>>> If they are cached, they can be easily returned from there. A first 
>>>>>>>>> shot at 
>>>>>>>>> a signature might look something like:
>>>>>>>>>
>>>>>>>>> getItem : GlobalState -> ID -> Tag -> (GlobalState, Cmd msg)
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> However we could partially apply functions that exist on some 
>>>>>>>>> globalState instantiation to hdie the initial state being passed in 
>>>>>>>>> and end 
>>>>>>>>> up with:
>>>>>>>>>
>>>>>>>>> state.items.getItem : ID -> Tag -> (GlobalState, Cmd msg)
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> The downside of this approach is that I have to thread this state 
>>>>>>>>> through multiple calls that might make use of it, and thread it back 
>>>>>>>>> up 
>>>>>>>>> explicitly through the update functions to get it back to the top. At 
>>>>>>>>> the 
>>>>>>>>> top we'd then have something like (excuse any mistakes!):
>>>>>>>>>
>>>>>>>>> update msg model = case msg of
>>>>>>>>>    SubMsg m ->
>>>>>>>>>      let (newSubModel, subCmds, newGlobalState) = SubComponent.update 
>>>>>>>>> m model.subModel
>>>>>>>>>      in ({ model | state = newGlobalState, subModel = newSubModel
>>>>>>>>> }, Sub.map SubMsg subCmds)
>>>>>>>>>    ...
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> An alternative approach is to hold this global state in an effect 
>>>>>>>>> manager, and so in the app you'd end up using the Cmd/Sub mechanism 
>>>>>>>>> to ask 
>>>>>>>>> for things from the state and internally initiate API requests to 
>>>>>>>>> update 
>>>>>>>>> the state as necessary. We'd end up with an API more like:
>>>>>>>>>
>>>>>>>>> getItem : ID -> Tag -> Cmd msg
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> or
>>>>>>>>>
>>>>>>>>> state.items.getItem : ID -> Tag -> Cmd msg
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> where the returned Cmd would either lead to an item being sent to 
>>>>>>>>> the component immediately via a cache (where Tag is a Msg type the 
>>>>>>>>> component knows about) or after it was obtained via some backend. 
>>>>>>>>> This 
>>>>>>>>> would make all retrieving of state async but seems to simplify the 
>>>>>>>>> interface (perhaps at the cost of more complexity in implementing the 
>>>>>>>>> effect manager).
>>>>>>>>>
>>>>>>>>> Which approach do people think is best for working with global 
>>>>>>>>> state (neither is an option if you have a better way!)? Do you get 
>>>>>>>>> away 
>>>>>>>>> with not needing this kind of thing (and if so, how)? I'd love to 
>>>>>>>>> hear 
>>>>>>>>> back, especially from those that have had experience building larger 
>>>>>>>>> apps 
>>>>>>>>> in Elm!
>>>>>>>>>
>>>>>>>>> -- 
>>>>>>>>> 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.
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> -- 
>>>>>>>> There is NO FATE, we are the creators.
>>>>>>>> blog: http://damoc.ro/
>>>>>>>>
>>>>>>> -- 
>>>>>>> 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.
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> -- 
>>>>>> There is NO FATE, we are the creators.
>>>>>> blog: http://damoc.ro/
>>>>>>
>>>>> -- 
>>>>> 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.
>>>>>
>>>>
>>>>
>>>>
>>>> -- 
>>>> There is NO FATE, we are the creators.
>>>> blog: http://damoc.ro/
>>>>
>>> ​
>>
>> -- 
>> 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] <javascript:>.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>
>
> -- 
> There is NO FATE, we are the creators.
> blog: http://damoc.ro/
>

-- 
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