0

As you may already know when you use map it takes a function and applies that function to every element of a list

map f xs is the list obtained by applying f to each element of xs, i.e.,

But in my case I have a list of functions that need to be applied in order to an specific value. These are the types, to give a better idea:

data Auto = Auto {plate :: String, currentTank :: Int, tanqSize :: Int} deriving (Show, Eq)
type Services = Car -> Car
service :: Car -> [Services] -> Car

As I explained before [Services] will be a list of functions that take in Car and return Car. I need to apply in order those functions to the Car that takes service and return it with all the modifications done.

Here are some examples of functions that may appear on the list:

emptyTank :: Services
reFuel :: Int -> Services
changePlate :: String -> Services
upgradeTank :: Int -> Services

Does anyone know a way to solve this? In case you know a more appropriate function to use instead of map, tell me and I'll look into it.

Eligum
  • 27
  • 7
Marco
  • 1,112
  • 1
  • 14
  • 34
  • Lookup `$`, `flip`, and currying. – PyRulez Apr 06 '16 at 01:49
  • 4
    Possible duplicate of [Haskell: mapping function application](http://stackoverflow.com/questions/13134857/haskell-mapping-function-application) – amalloy Apr 06 '16 at 01:55
  • 3
    This isn't a duplicate of the suggestion; `service` needs to return a single `Car`, not a list of type `[Car]`. – chepner Apr 06 '16 at 02:55
  • Please read "[ask]" and "[Stack Overflow question checklist](https://meta.stackoverflow.com/questions/260648)" along with "[mre]" and their linked pages. Did you research this? Where? If it didn't help tell us why. What did you try? If you didn't try, why not? If you did, what did you do? We'd like to see your minimal attempt to solve it. – the Tin Man Feb 26 '22 at 07:14

2 Answers2

2

You can use foldl for this:

service :: Car -> [Services] -> Car
service car functions = foldl (flip ($)) car functions

service someCar [emptyTank, (refuel 10), (changePlate "abc 123"), (upgradeTank 15)]

($) is function application, but to use it with foldl (to apply the functions in the correct order, we need to flip its arguments so that arg $ f evaluates to f arg. Using (flip ($)) with foldl will cause the first function in functions to be applied to car, then the second function will be applied to the result of the first, then the third to the result of the second, etc.

(foldr could be used with simply ($) instead of flip ($), but it would apply the functions from right to left, not left to right. Compare

foldl (flip ($)) 3 [(+4), (*5)]  -- Returns (3+4)*5 = 35

with

foldr ($) 3 [(+4), (*5)] -- Returns (3*5) + 4 = 19

)


Another way of looking at this is that you want to compose your list of functions into one function:

(upgradeTank 15) . (changePlate "abc 123") . (refuel 10) . emptyTank $ someCar

which you can also do with foldl (or foldr? It doesn't seem to make a difference which you use here), using (.) to reduce the list to a single function, then applying that function to car. The id function is used as the other argument for the first call to (.) made by foldl. You do need to reverse the list of functions first, though, since composition is right-associative.

service car functions = foldl (.) id (reverse functions) $ car
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Thanks, here is how i used what you told me: Services (Car plate currentTanq tanqSize) list = foldl (flip ($)) (Car plate currentTanq tanqSize) list – Marco Apr 06 '16 at 12:35
0

The fold answer is pretty accurate and I think is the best, but I have found an alternative which isn't too much slower only 0.003% less scalability. Using pattern matching and recursion.

apply_all x [] = x
apply_all x function_list = apply_all ((head function_list) x) (tail function_list)
Bruno Rohée
  • 3,436
  • 27
  • 32
ethereumUser
  • 11
  • 1
  • 2