Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added List.foldlWhile function #89

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open

added List.foldlWhile function #89

wants to merge 5 commits into from

Conversation

AIRTucha
Copy link

@AIRTucha AIRTucha commented Jan 4, 2018

List.foldlWhile function seems to be useful in two practical cases:

  • Reduction while accumulation value satisfies certain criteria
  • More optimized solution for a situation, when a reduction is needed only until the first unacceptable value in the list.
    list 
        |> takeWhile someFunction1
        |> foldl someFunction2 someInitialValue

Accumulation of pixels in a grey scale image is a good example of the function use case.

    accomulateColor colorSum pixelColor =
        let
            newColorSum = colorSum + pixelColor
        in
            if newColorSum > 255 the
                Nothing
            else 
                Just newColorSum

    [10, 55, 63, 78]
        |> foldlWhile accomulateColor 0

@Chadtech
Copy link
Collaborator

Chadtech commented Jan 5, 2018

Hey @AIRTucha ,

Could you elaborate about your use case a bit? In what kind of context would one be adding up gray scale values until they are 255?

foldWhile seems like a substitute for recursion to me. So either this function has to beat simple recursion in its usefulness, or it provides something different from recursion that I am not yet seeing.

@AIRTucha
Copy link
Author

AIRTucha commented Jan 14, 2018

Hey @Chadtech, I am sorry for a long response, I was a little bit busy.

I might get you wrong, but all these functions are substitutes for recursion in some sense.

So, as I said before, there are two cases:

  1. It is faster solution for
list 
        |> takeWhile someFunction1
        |> foldl someFunction2 someInitialValue

The performance is improved by: prevention of an intermediate creation and lower number of function calls. It should also reduce a pressure on GC.

  1. You need to accumulate data from the list, but you want to stop if a certain condition is achieved. For example, you have a list of buckets and you wanna fill a tank. So, you do:
type alias Litre = Float

type alias Tank = 
  { level: Litre
  , volume: Litre
  }

add2Tank tank bucket =
  let
    newLevel = tank.level + bucket
  in
    if tank.volume > newLevel then
      Just newLevel
    else 
      Nothing

buckets: List Litre
buckets = 
  [ 1.5, 1.2, 2.0 ]

tank =                                                   -- { level: 4.7, volume: 5 }
  buckets
    |> add2Tank { level: 0.4, volume: 5 }

Of course, it can be easily done by recursion, but several times, I faced a situation when I want such a function.

@Chadtech
Copy link
Collaborator

Hey @AIRTucha , sorry for the delay in my reply. Heres a comparison as I see it, between using recursion and foldWhile

type alias Tank =
    { level : Float
    , volume : Float
    }

buckets : List Float
buckets =
    [ 1.5, 1.2, 2.0 ]

tank : Tank
tank =
    { level : 0.4 
    , volume : 5
    }

-- Recursion
fillTank : List Bucket -> Tank -> Tank
fillTank buckets tank =
    case buckets of
        first :: rest ->
            if first + tank.level > tank.volume then
                tank
            else
                fillTank { tank | level = tank.level + first } rest

        [] ->
            tank

-- foldWhile
fillTank : List Bucket -> Tank -> Tank
fillTank buckets tank =
    List.Extra.foldWhile addToTank tank buckets

addToTank : Bucket -> Tank -> Maybe Tank
addToTank bucket tank =
    if first + tank.level > tank.volume then
        Nothing
    else
        Just tank

I see the recursive technique at least as good at the foldWhile technique. Here are a few disadvantages that I see in the foldWhile technique:

0 The Maybe in the foldWhile technique hurts readability. Someone who is seeing the code for the first time might not understand the meaning of the Maybe, making it that much harder to grasp the purpose of the code at a glance.
1 The foldWhile technique requires List.Extra

@pzp1997
Copy link
Contributor

pzp1997 commented Jan 31, 2018

Great example @Chadtech. Just wanted to chime in that in addition to hurting readability, using Maybes in lieu of actual recursion most likely negatively affects performance.

@AIRTucha
Copy link
Author

AIRTucha commented Feb 5, 2018

Wait a minute, you can eliminate foldl by the exact same way, can't you?

Ok, another possible case, actually, it was one of the reasons I wrote it here. So, I was working on some kind of parser. One of the key places in my project was folding over lines, parsing of each of them a combining them together. Parsing of each next line was based on a result previous one, so if it fails it has to stop and output the successful part.

I assume that there are some other case on which the same pattern can be applied.

Anyway, it is "extra", I used to think that the main idea here is to collect and experimental features and see if people are going to use them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants