Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameter names of a function using a functional language such as F#

I have read a book called Clean code. One of the strongest messages that I took from the book is that the code must be readable. I do not understand why functional languages such as F# do not include function parameter names into the intellisense or in the type defintion?

Compare

val copy: string -> string -> unit

with

val copy: sourceFile:string -> destinationFile:string -> unit

What is the best practise in the functional world? Shall we prefer single parameter functions? This is what Clean code promotes. Or shall we use records for all functions of 2+ parameters?

I know that one work-around is to use static member instead of let functions but this is not a functional approach, is it?

EDIT:

Just to give more information:

  • Haskell : addThree :: Int -> Int -> Int -> Int
  • OCaml: Unix.unlink: (string -> unit)

and surely others. They just do not show parameter names in the type definitions.

like image 610
Oldrich Svec Avatar asked Feb 26 '13 11:02

Oldrich Svec


People also ask

What are the parameters of a function called?

The parameters, in a function call, are the function's arguments.

What kind of variables are the parameters of a function?

A parameter is a special kind of variable used in a function to refer to one of the pieces of data provided as input to the function. These pieces of data are the values of the arguments with which the function is going to be called/invoked.

What is functional programming language with example?

Functional programming languages are specially designed to handle symbolic computation and list processing applications. Functional programming is based on mathematical functions. Some of the popular functional programming languages include: Lisp, Python, Erlang, Haskell, Clojure, etc.

What is parameter and list its types?

The variables used in the function definition as parameters are known as formal parameters. The constants, variables, or expressions used in the function call are known as actual parameters. Types: Default arguments and Constant Arguments. Concept: User-defined Functions.


2 Answers

If you practice typeful programming, which is the idea that a lot of the semantic content of programs can be reflected statically in the type system, you will find out that in many (but not all) cases, named arguments are not necessary for readability.

Consider the following examples in the List standard library of OCaml. By knowing that they operate on lists, and with the (hopefully clear: we're all for good name choices) name of the function, you will find that you don't need explanations for what the arguments do.

val append : α list -> α list
val flatten : α list list -> α list
val exists: (α -> α bool) -> α list -> bool
val map: (α -> β) -> α list -> β list
val combine : α list -> β list -> (α * β) list

Note that the last example is interesting because it is not exactly clear what the code will do. There would in fact be several interpretations. combine [1;2] [3;4] returns [(1,3); (2,4)] and not, for example, [(1,3); (1,4); (2,3); (2,4)]. It is also not clear what happens when the two lists are not of the same length (the failure mode is unclear).

Functions that are not total, that may raise an exception or not terminate, usually need more documentation about what the failure cases are and how they will behave. This is one strong argument in favor of what we call pure programming, where all the behavior of a function is expressed in terms of returning a value (no exceptions, observable state mutation, or non-termination), and can therefore be statically captured by the type system.

Of course this only works really well for functions that are parametric enough; they have a type that make it very clear what they do. This is not the case of all functions, consider for example the blit function of the String module (I'm sure your favorite language has such examples as well):

val blit : string -> int -> string -> int -> int -> unit

huh?

Programming languages add support named parameters for this reason. In OCaml for example we have "labels" that allow to name parameters. The same function is exported in the StringLabels module as:

val blit : src:string -> src_pos:int -> dst:string -> dst_pos:int -> len:int -> unit

That's better. Yes, named parameters are useful in some cases.

Note however that named arguments can be used to hide bad API design (maybe the example above is targe to this criticism as well). Consider:

val add : float -> float -> float -> float -> float -> float -> float * float * float

obscure, huh? But then:

type coord = {x:float; y:float; z:float}
val add : coord -> coord -> coord

That's much better, and I didn't need any parameter labeling (arguably record labels provide naming, but in fact I could equally use float * float * float here; the fact that value records may subsume named (and optionals?) parameters is also an interesting remark).

David M. Barbour develops the argument that named parameters are a "crutch" of language design, that is used to tamper over the lazyness of API designers, and that not having them encourages better design. I am not sure I agree that named parameters can be profitably avoided in all situations, but he certainly has a point that agrees with the typeful propaganda at the beginning of my post. By raising the level of abstraction (through more polymorphic/parametric types or better problem domain abstractions), you'll find you decrease the need for parameter naming.

like image 173
gasche Avatar answered Oct 08 '22 05:10

gasche


Haskell's type synonyms can help in making the type signatures more self-documenting. Consider for example the function writeFile, that just writes a string to a file. It has two parameters: the string to write, and the filename to write to. Or was it the other way around? Both parameters are of type String, so it's not easy to tell which is which!

However, when you look at the documentation, you see the following type signature:

writeFile :: FilePath -> String -> IO ()

That makes it clear (to me, at least!) how the function is intended to be used. Now, since FilePath is just a synonym for String, there's nothing preventing you from using it like this:

writeFile "The quick brown fox jumped over the lazy dog" "test.txt"

but if you get the type FilePath -> String -> IO () as a hint in your IDE, I think that's at least a big push in the right direction!

You could even go a step further and make a newtype for filepaths so you don't accidentally mix up filenames and contents, but I guess that adds more hassle than it's worth, and there's probably also historical reasons why this isn't done.

like image 7
yatima2975 Avatar answered Oct 08 '22 04:10

yatima2975