Here is the implementation of the Clojure's complement function:
(defn complement
"Takes a fn f and returns a fn that takes the same arguments as f,
has the same effects, if any, and returns the opposite truth value."
{:added "1.0"
:static true}
[f]
(fn
([] (not (f)))
([x] (not (f x)))
([x y] (not (f x y)))
([x y & zs] (not (apply f x y zs)))))
Why is it defined as a multi-arity function? The following implementation, it seems to me, would achieve the same result:
(defn alternative-complement [f]
(fn [& args] (not (apply f args))))
For what reason, does the Clojure's complement treat none, single and two arguments as "special cases"?
Under the hood, when you use Java's variable arguments feature, the compiler
creates an array for the arguments. There is a performance penalty associated with this array creation.
In order to eliminate this performance penalty, specific arities are added for the most commonly used cases, optimizing for these. If it can be determined through some means that complement is used on functions which take zero, one or two arguments, most of the time, then it's deemed worth the effort to add specific arities for zero, one, and two argument functions. Functions which take more than two arguments are supported but may incur a minor performance penalty.
Java's own core library makes use method overloading to eliminate the performance hit of varargs. See Java 11's List which contains twelve overloads for the static of method, maxing out at allowing ten elements.
static <E> List<E> of(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10) {
return ImmutableCollections.listFromTrustedArray(e1, e2, e3, e4, e5,
e6, e7, e8, e9, e10);
}
Related
Functional Programming in C++, at page 214, with reference to an expected<T,E> monad which is the same as Haskell's Either, reads
[...] as soon as any of the functions you're binding to returns an error, the execution will stop and return that error to the caller.
Then, in a caption just below, it reads
If you call mbind [equivalent to Haskell's >>=] on an expected that contains an error,, mbind won't even invoke the transformation function; it will just forward that error to the result.
which seems to "adjust" what was written earlier. (I'm pretty sure that either LYAH or RWH underlines somewhere that there's no short-circuiting; if you remember where, please, remind me about it.)
Indeed, my understanding, from Haskell, is that in a chain of monadic bindings, all of the bindings happen for real; then what they do with the function passed to them as a second argument, is up to the specific monad.
In the case of Maybe and Either, when the bindings are passed a Nothing or Left x argument, then the second argument is ignored.
Still, in these specific two cases, I wonder if there's a performance penalty in doing something like this
justPlus1 = Just . (+1)
turnToNothing = const Nothing
Just 3 >>= turnToNothing >>= justPlus1
>>= justPlus1
>>= justPlus1
>>= justPlus1
>>= justPlus1
as in these cases the chain can't really do anything other than what it does, given that
Nothing >>= _ = Nothing
Left l >>= _ = Left l
Consider the following expression:
result :: Maybe Int
result = x >>= f >>= g >>= h
In that expression, of course, x :: Maybe a for some a, and each of f, g, and h are functions, with h returning Maybe Int but the intermediate types of the pipeline could be anything wrapped in a Maybe. Perhaps f :: String -> Maybe String, g :: String -> Maybe Char, h :: Char -> Maybe Int.
Let's make the associativity explicit as well:
result :: Maybe Int
result = ((x >>= f) >>= g) >>= h
To evaluate the expression, each bind (>>=) must in fact be called, but not necessarily the functions f, g, or h. Ultimately the bind to h needs to inspect its left-hand argument to decide whether it is Nothing or Just something; in order to determine that we need to call the bind to g, and to decide that we need to call the bind to f, which must at least look at x. But once any one of these binds produces Nothing, we pay only for checking Nothing at each step (very cheap), and not for calling the (possibly expensive) downstream functions.
Suppose that x = Nothing. Then the bind to f inspects that, sees Nothing, and doesn't bother to call f at all. But we still need to bind the result of that in order to know if it's Nothing or not. And this continues down the chain until finally we get result = Nothing, having called >>= three times but none of the functions f, g, or h.
Either behaves similarly with Left values, and other monads may have different behaviors. A list may call each function one time, many times, or no times; the tuple monad calls each function exactly once, having no short-circuiting or other multiplicity features.
You seem to have a misunderstanding of how the Monad instances for these types work in Haskell. You say:
Indeed, my understanding, from Haskell, is that in a chain of monadic functions, all of the functions are called,
But this demonstrably isn't the case. Indeed any time you compute
Nothing >>= f
where f is any function of type a -> Maybe b, then it is computed according to the implementation of >>= for the Maybe monad, which is:
Just x >>= f = f x
Nothing >>= f = Nothing
So f will indeed be called in the Just case, but not in the Nothing case. So we see that there is indeed "short circuiting". Indeed, since Haskell is lazy, every function "short circuits" by default - nothing is ever computed unless it's needed to produce a result.
It's an interesting question to ask about performance - and not a question I personally know how to answer. Certainly, as I just explained, none of the following functions in the chain will be evaluated once a Nothing is encountered - but performing the pattern matching to see this is unlikely to be free. Perhaps the compiler is able to optimise this away, by reasoning that it can abandon the entire calculation once it hits a Nothing. But I'm not sure.
Perhaps neither of these statements are categorically precise, but a monad is often defined as "a monoid in the category of endofunctors"; a Haskell Alternative is defined as "a monoid on applicative functors", where an applicative functor is a "strong lax monoidal functor". Now these two definitions sound pretty similar to the ignorant (me), but work out significantly differently. The neutral element for alternative has type f a and is thus "empty", and for monad has type a -> m a and thus has the sense "non-empty"; the operation for alternative has type f a -> f a -> f a, and the operation for monad has type (a -> f b) -> (b -> f c) -> (a -> f c). It seems to me that the real important detail is in the category of endofunctors versus over endofunctors, though perhaps the "strong lax" detail in alternative is important; but that's where I get confused because within Haskell at least, monads end up being alternatives: and I see that I do not yet have a precise categorical understanding of all the details here.
How can it be precisely expresseed what the difference is between alternative and monad, such that they are both monoids relating to endofunctors, and yet the one has an "empty" neutral and the other has a "non-empty" neutral element?
In general, a monoid is defined in a monoidal category, which is a category that defines some kind of (tensor) product of objects and a unit object.
Most importantly, the category of types is monoidal: the product of types a and b is just a type of pairs (a, b), and the unit type is ().
A monoid is then defined as an object m with two morphisms:
eta :: () -> m
mu :: (m, m) -> m
Notice that eta just picks an element of m, so it's equivalent to mempty, and curried mu becomes mappend of the usual Haskell Monoid class.
So that's a category of types and functions, but there is also a separate category of endofunctors and natural transformations. It's also a monoidal category. A tensor product of two functors is defined as their composition Compose f g, and unit is the identity functor Id. A monoid in that category is a monad. As before we pick an object m, but now it's an endofunctor; and two morphism, which now are natural transformations:
eta :: Id ~> m
mu :: Compose m m ~> m
In components, these two natural transformations become:
return :: a -> m a
join :: m (m a) -> m a
An applicative functor may also be defined as a monoid in the functor category, but with a more sophisticated tensor product called Day convolution. Or, equivalently, it can be defined as a functor that (laxly) preserves monoidal structure.
Alternative is a family of monoids in the category of types (not endofunctors). This family is generated by an applicative functor f. For every type a we have a monoid whose mempty is an element of f a and whose mappend maps pairs of f a to elements of f a. These polymorphic functions are called empty and <|>.
In particular, empty must be a polymorphic value, meaning one value per every type a. This is, for instance, possible for the list functor, where an empty list is polymorphic in a, or for Maybe with the polymorphic value Nothing. Notice that these are all polymorphic data types that have a constructor that doesn't depend on the type parameter. The intuition is that, if you think of a functor as a container, this constructor creates and empty container. An empty container is automatically polymorphic.
Both concepts are tied to the idea of a "monoidal category", which is a category you can define the concept of a monoid in (and certain other kinds of algebraic structures). You can think of monoidal categories as: a category defines an abstract notion of functions of one argument; a monoidal category defines an abstract notion of functions of zero arguments or multiple arguments.
A monad is a monoid in the category of endofunctors; in other words, it's a monoid where the product (a function of 2 arguments) and the identity (a function of 0 arguments) use the concept of multi-argument function defined by a particular (bizarre) monoidal category (the monoidal category of endofunctors and composition).
An applicative functor is a monoidal functor. In other words, it's a functor that preserves all the structure of a monoidal category, not just the part that makes it a category. It should be obvious that that means it has mapN functions for functions with any number of arguments, not just functions of one argument (like a normal functor has).
So a monad exists within a particular monoidal category (which happens to be a category of endofunctors), while an applicative functor maps between two monoidal categories (which happen to be the same category, hence it's a kind of endofunctor).
To supplement the other answers with some Haskell code, here is how we might represent the Day convolution monoidal structure #Bartosz Milewski refers to:
data Day f g a = forall x y. Day (x -> y -> a) (f x) (g y)
With the unit object being the functor Identity.
Then we can reformulate the applicative class as a monoid object with respect to this monoidal structure:
type f ~> g = forall x. f x -> g x
class Functor f => Applicative' f
where
dappend :: Day f f ~> f
dempty :: Identity ~> f
You might notice how this rhymes with other familiar monoid objects, such as:
class Functor f => Monad f
where
join :: Compose f f ~> f
return :: Identity ~> f
or:
class Monoid m
where
mappend :: (,) m m -> m
mempty :: () -> m
With some squinting, you might also be able to see how dappend is just a wrapped version of liftA2, and likewise dempty of pure.
The first and the second flatMap work well. Why doesn't the third one work?
fun flatMap f xs = List.concat(List.map f xs)
fun flatMap f = List.concat o List.map f
val flatMap = (fn mmp => List.concat o mmp) o List.map;
This is due to a rule called "value polymorphism" or the "value restriction". According to this rule, a value declaration can't create a polymorphic binding if the expression might be "expansive"; that is, a value declaration can only create a polymorphic binding if it conforms to a highly restricted grammar that ensures it can't create ref cells or exception names.
In your example, since (fn mmp => List.concat o mmp) o List.map calls the function o, it's not non-expansive; you know that o doesn't create ref cells or exception names, but the grammar can't distinguish that.
So the declaration val flatMap = (fn mmp => List.concat o mmp) o List.map is still allowed, but it can't create a polymorphic binding: it has to give flatMap a monomorphic type, such as (int -> real list) -> int list -> real list. (Note: not all implementations of Standard ML can infer the desired type in all contexts, so you may need to add an explicit type hint.)
This restriction exists to ensure that we don't implicitly cast from one type to another by writing to a ref cell using one type and reading from it using a different type, or by wrapping one type in a polymorphic exception constructor and unwrapping it using a different type. For example, the below programs are forbidden by the value restriction, but if they were allowed, each would create a variable named garbage of type string that is initialized from the integer 17:
val refCell : 'a option ref =
ref NONE
val () = refCell := SOME 17
val garbage : string =
valOf (! refCell)
val (wrap : 'a -> exn, unwrap : exn -> 'a) =
let
exception EXN of 'a
in
(fn x => EXN x, fn EXN x => x)
end
val garbage : string =
unwrap (wrap 17)
For more information:
"ValueRestriction" in the MLton documentation
"Value restriction" on the English Wikipedia
"Types and Type Checking" in SML/NJ's guide to converting programs from Standard ML '90 to Standard ML '97. (Standard ML '90 had a different version of this rule, that was more permissive — it would have allowed your program — but considered "somewhat subtle" and in some cases "unpleasant", hence its replacement in Standard ML '97.)
the following sections of The Definition of Standard ML (Revised) (PDF):
§4.7 "Non-expansive Expressions", page 21, which defines which expressions are considered "non-expansive" (and can therefore be used in polymorphic value declarations).
§4.8 "Closure", pages 21–22, which defines the operation that makes a binding polymorphic; this operation enforces the value restriction by preventing the binding from becoming polymorphic if the expression might be expansive.
inference rule (15), page 26, which uses the aforementioned operation; see also the comment on page 27.
the comment on inference rule (20), page 27, which explains why the aforementioned operation is not applied to exception declarations. (Technically this is somewhat separate from the value restriction; but the value restriction would be useless without this.)
§G.4 "Value Polymorphism", pages 105–106, which discusses this change from Standard ML '90.
The list monad is given here. Also see Spivak's paper here. So list is a monad. Is it a comonad? How would you prove that?
The list type constructor a ↦ μ L. 1 + a * L doesn't admit a comonad structure. Recall that if it were a comonad, we'd have (using the names from Haskell's Functor and Comonad typeclasses)
fmap :: ∀ a b. (a → b) → [a] → [b]
extract :: ∀ a. [a] → a
duplicate :: ∀ a. [a] → [[a]]
However, even without going into any required laws, extract cannot be implemented, since its input could be the empty list, giving no way to come up with an a.
The nonempty list type constructor a ↦ μ NE. a + a * NE does admit a comonad structure, with extract returning the first element, and duplicate mapping [x, y, ..., z] to [[x], [x, y], ..., [x, y, ..., z]] (note that each of them are non-empty by construction).
I need to create a function rec assoc (d,k,l) that takes a triple (d,k,l) where l is a list of key-value pairs [(k1,v1);(k2,v2);...] and finds the first ki that equals k. If such a ki is found, then vi is returned. Otherwise, the default value d is returned. it needs to be tail recursive
here is the function that I made:
let rec assoc (d,k,l) = match l with
|[]-> d
|(a,b)::t ->
if (a==k) then b
else assoc(d,k,t)
;;
my logic here is take the head of the list l and if the first part of the tuple in the list matches k, I return the second part of the tuple.
If not then i want to call the function again on the tail of the list so it checks each element. If the entire list is traversed down to the empty list without finding a match, I want to return d. For some reason, it always returns d no matter what list I give it. What could be the reason for that.
heres some sample output it should give:
# assoc (-1,"jeff",[("sorin",85);("jeff",23);("moose",44)]);;
- : int = 23
# assoc (-1,"bob",[("sorin",85);("jeff",23);("moose",44)("margaret",99)]);;
- : int = -1
mine returns -1 for both
Don't use == for comparisons. It's a special-purpose "physical equality". Use = for comparisons.
(Other than this your code looks excellent.)
Comparison operators are defined in the Pervasives module. Here are the descriptions of = (the normal equality comparison) and == (the physical equality comparison):
e1 = e2 tests for structural equality of e1 and e2. Mutable structures (e.g. references and arrays) are equal if and only if their current contents are structurally equal, even if the two mutable objects are not the same physical object. Equality between functional values raises Invalid_argument. Equality between cyclic data structures may not terminate.
e1 == e2 tests for physical equality of e1 and e2. On mutable types such as references, arrays, byte sequences, records with mutable fields and objects with mutable instance variables, e1 == e2 is true if and only if physical modification of e1 also affects e2. On non-mutable types, the behavior of ( == ) is implementation-dependent; however, it is guaranteed that e1 == e2 implies compare e1 e2 = 0.
Generally speaking, you shouldn't use == in your code unless you want the specific (weak) equality test described here. Or, just don't use it :-)