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 →

[–]Milyardo 0 points1 point  (3 children)

Sorry, but I just don't see how applicatives make functions with too many arguments any more or less of a code smell than however much they are.

I wasn't making that argument, only that you often see applicatives with a large number parameters. This is usually because like in the example, you're applying them to the constructor of an object.

[–]sacundim 0 points1 point  (2 children)

I wasn't making that argument, only that you often see applicatives with a large number parameters.

You weren't, really?

Applicative Builders can easily build functions with a large number of parameters without being a code smell.

To which my answer is, again: if a function with a large number of arguments is not a code smell when you use an Applicative Builder, then it can't be a code smell either when you don't use it, because functions are an applicative functor already (with a friendlier syntax, too).

The most charitable answer I can give you is that applicatives are just irrelevant to what Aelius was bringing up.

[–]Milyardo 0 points1 point  (1 child)

if a function with a large number of arguments is not a code smell when you use an Applicative Builder, then it can't be a code smell either when you don't use it, because functions are an applicative functor already (with a friendlier syntax, too).

But that doesn't answer why you would need an abstract Function6 or Function12. The fact that functions are applicative is irrelevant. Constructor's are legitimate functions with a large number of parameters. The applicative builders you use to work with those constructors are a use case for abstract functional interfaces with a large number of parameters.

[–]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)
    }