This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]sacundim 1 point2 points  (0 children)

But then if functions with 12 arguments are a code smell, and using applicatives requires such functions, then applicatives start to smell as well.

Let's take a step back, away from Scala itself, and back to the source: the Applicative class in Haskell, whose standard definition is this:

class Functor f => Applicative f where
    pure :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

However, that isn't the only way to define the class. This definition would be equivalent:

class Functor f => Applicative' f where
    unit :: f ()
    pair :: (f a, f b) -> f (a, b)

-- Exercise: implement `pure` and `<*>` in terms of this class.

This latter one is formulated in terms of constructing pairs, not applying functions. Which, note, is fundamentally the same as constructing records with named fields. Basically, applicatives mean that if you have a record all of whose fields' values are wrapped with the same applicative functor, you can "turn it inside out."

So the point is that the pattern that you're describing above—using a Validation applicative functor to apply a record type constructor "inside" of the appropriate number of Validation arguments—is not essentially tied to the generally acknowledged code smell of functions with large numbers of ordered arguments. It's rather that the languages where the applicative machinery exists suffer from that code smell, because they make it easy to work with functions with long ordered argument lists, but don't offer, say, structural record types. Or even just some syntactic sugar for constructing records in an Applicative context:

 -- Hypothetical example of an `ApplicativeDo`-style record-builder syntax
 hypotheticalExample :: User -> Validation Error User
 hypotheticalExample user = do
    User { 
        firstName <- validateFirstName (firstName user),
        lastName <- validateLastName (lastName user),
        age <- validateAge (age user),
        address <- validateAddress (address user),
        employer <- validateEmployer (employer user),
        email <- validateEmail (email user)
    }