Tim, thank you for pointing this out.

In the interest of clarity I would like to add that ! is not an operator
but a function.

Elm allows one to define single character functions and if the function
name is made of symbols, it can be used inline without backtick marks (you
have to declare the name inside parentheses).

! first appeared in 0.17 so it makes sense to be new for a lot of people.
I also can see how this is something that might require some getting used
to.

I used to have a function in my code called "noFx" that I would use to
avoid typing a lot of Effects.none in update but I find this infix function
to be much nicer.
Even in the case of returning a single Cmd (where I'm not saving any
characters) I found myself preferring this form for uniformity reasons.

Anyway, thank you again for bringing this to my attention.
I now see that Evan's examples (where I first encountered this pattern)
moved to a more explicit (model, Cmd.none).
I'll try to do the same. :)





On Thu, Jun 2, 2016 at 3:52 AM, Tim Stewart <[email protected]>
wrote:

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

Reply via email to