Your final example (exponential backoff on HTTP requests) doesn't need any 
magic.

You can use `Http.toTask` to turn it into a task and implement a generic 
"retry task at most x times with backoff" like this:

retry : Int -> Time -> Task x a -> Task x a
retry maxTries backOff task =
    if maxTries == 0 then
        task
    else
        Task.onError
            (\_ ->
                Process.sleep backOff
                    |> Task.andThen
                        (\_ -> retry (maxTries - 1) (backOff * 2) task)
            )
            task


Op zondag 29 oktober 2017 00:30:45 UTC+2 schreef Henry:
>
> Thank you Ryan for the excellent reply! You made a lot of good points, and 
> I appreciate the overview on the available libraries and their drawbacks. 
>
> I feel like the effect manager conveys the intent of the code better, you 
> get to say "I want this to happen" and then shove the state in a lock box, 
> throw it in a closet, throw the closet in a river, and pretend like the 
> hole in the side of your house was always there, and is perfectly normal. 
> The use of global string IDs is a big downside though, it feels dirty 
> writing code like that when everything else is wrapped in ribbons, laced 
> with foil, and covered in the wondrous glitter of the type system.
>
> With relating effects to time, the main examples I think of are: 
> debouncing searches, throttling clicks/events (you can only do X once every 
> 10 seconds), exponential backoff (for network requests).  I've asked a few 
> people if they know the name of that class of problems, relating events to 
> time, and I still don't know the name of them, but some friends now think 
> I'm an idiot who doesn't know what "history" is.
>
> I was trying to think of various ways it could look in the language (with 
> little regard to what is feasible...), and these are a few
>
> let
>     debouncedInput = debounce 200 onInputininput [ debouncedInput 
> SearchOnServer ]
>
>
> This way the message and model are unchanged, and debouncedInput handles 
> everything auto-magically, much the same way onInput does already.  
> Ideally you could use that with Html.Events.on, or any of its ilk.  The 
> upside here is that it is very clear what is happening, because you get to 
> state it right where it happens, but it would need to be implemented in 
> VirtualDom and it would only work for HTML events.  It could be use for 
> throttling chat messages, button clicks, scroll events, etc.  The event 
> listener implementation is in VirtualDom here (
> https://github.com/elm-lang/virtual-dom/blob/dev/src/Elm/Kernel/VirtualDom.js#L521-L527
> )
>
> Another potential way (ala unbounce or mceldeen's implementations) is 
> handling the individual message, but debouncing a subsequent message.  We 
> can collect the text from the search bar, and run the search after the user 
> has quit typing.
>
> update : Msg -> Model -> ( Model, Cmd Msg )update msg model =
>     case msg of
>         SearchUpdate newSearch ->
>             ( { model | search = newSearch }, debounce "search" 200 
> PerformSearch )
>
>
> I feel it would be even better to be able to write as
>
> ( { model | search = newSearch }, debounce 200 PerformSearch )
>
>
> For HTTP requests I would use the "second message' pattern as well, with 
> one event sending a Cmd that will resolve later and then make the 
> request.   With exponential backoff there is added state for the number of 
> tries so far, so my ideal API handles all of that for me with magic
>
> Http.getWithExponentialBackoffAndAMakeAMartini decoder 
> ("https://grass-fed-lemonade.com/search"; ++ query)
>
>
> But a more realistic version is probably something like
>
> update : Msg -> Model -> ( Model, Cmd Msg )update msg model =
>     case msg of
>         SearchFailed err ->
>             ( { model | failed = model.failed + 1 }
>             , Process.sleep (model.failed * 2 * 1000)
>                   |> Task.perform (\_ -> PerformSearch)
>
>
> I think any inclusion of "magic" (re: hidden state), should be weighed 
> carefully, and in this instance I think the increased clarity of the code 
> is worth it, though clarity is subjective as well.
>
> On Friday, October 13, 2017 at 5:23:45 PM UTC-4, Ryan Rempel wrote:
>>
>> On Thursday, October 5, 2017 at 9:27:27 AM UTC-5, Henry wrote:
>>>
>>> Evan has said before that there are around 10 valid uses of an effect 
>>> manager, do you think relating effects to time is one of them?
>>>
>>
>> One way of thinking about this question is to compare existing debouncers 
>> that use an effects manager vs. those which do not.
>>
>> Two debouncers which use an effects manager are:
>>
>> https://github.com/unbounce/elm-debounce
>> https://github.com/mceldeen/elm-debouncer
>>
>> Two debouncers which do not use an effects manager are:
>>
>> http://package.elm-lang.org/packages/jinjor/elm-debounce/latest
>> http://package.elm-lang.org/packages/mpizenberg/elm-debounce/latest
>>
>> Now, both approaches need to keep some state, of course.
>>
>> The advantage of an effects manager is that it can keep some state behind 
>> the scenes, so to speak, without requiring the user of the package to 
>> integrate the state into the `model`, `msg` and `update` scheme in the 
>> usual way.
>>
>> However, I don't think there is anything an effects manager can 
>> accomplish (at least with respect to debouncing) that cannot also be done 
>> without an effects manager, at the cost of additional wiring and verbosity. 
>> That is, at the cost of making the state visible, and requiring you to 
>> integrate it into your `model`, `msg` and `update` scheme, you can achieve 
>> everything you'd want with respect to debouncing without using an effects 
>> module.
>>
>> In fact, there are some advantages in not using an effects module. If you 
>> look at the debouncers that use an effects module, they employ a string ID 
>> in order to distinguish between one debouncer and another. (That is, the 
>> module is possibly tracking the state for multiple debouncers internally, 
>> so you need to provide a string ID to distinguish between one and another). 
>> But, of course, this isn't entirely satisfactory, since it forces you to 
>> maintain some scheme of globally-unique strings within your program. (Which 
>> is not necessarily that hard, really, but it is something which you'd 
>> normally want to avoid).
>>
>> That problem doesn't arise if you have to explicitly integrate some state 
>> into your `model`, `msg` and `update` in the usual way, since then you just 
>> provide the relevant state when needed ... you can't accidentally refer to 
>> the wrong bit of state by providing a string ID that is also used elsewhere.
>>
>> Of course, there may be a clever way to write an effects module that 
>> avoids this problem.
>>
>

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