The *args
and **kwargs
is a common idiom to allow arbitrary number of arguments to functions as described in the section more on defining functions in the Python documentation.
The *args
will give you all function parameters as a tuple:
def foo(*args):
for a in args:
print(a)
foo(1)
# 1
foo(1,2,3)
# 1
# 2
# 3
The **kwargs
will give you all
keyword arguments except for those corresponding to a formal parameter as a dictionary.
def bar(**kwargs):
for a in kwargs:
print(a, kwargs[a])
bar(name='one', age=27)
# name one
# age 27
Both idioms can be mixed with normal arguments to allow a set of fixed and some variable arguments:
def foo(kind, *args, **kwargs):
pass
It is also possible to use this the other way around:
def foo(a, b, c):
print(a, b, c)
obj = {'b':10, 'c':'lee'}
foo(100,**obj)
# 100 10 lee
Another usage of the *l
idiom is to unpack argument lists when calling a function.
def foo(bar, lee):
print(bar, lee)
l = [1,2]
foo(*l)
# 1 2
In Python 3 it is possible to use *l
on the left side of an assignment (Extended Iterable Unpacking), though it gives a list instead of a tuple in this context:
first, *rest = [1,2,3,4]
first, *l, last = [1,2,3,4]
Also Python 3 adds new semantic (refer PEP 3102):
def func(arg1, arg2, arg3, *, kwarg1, kwarg2):
pass
Such function accepts only 3 positional arguments, and everything after *
can only be passed as keyword arguments.
Note:
- A Python
dict
, semantically used for keyword argument passing, are arbitrarily ordered. However, in Python 3.6, keyword arguments are guaranteed to remember insertion order.
- "The order of elements in
**kwargs
now corresponds to the order in which keyword arguments were passed to the function." - What’s New In Python 3.6
- In fact, all dicts in CPython 3.6 will remember insertion order as an implementation detail, this becomes standard in Python 3.7.
- A static variable inside a function keeps its value between invocations.
- A static global variable or a function is "seen" only in the file it's declared in
(1) is the more foreign topic if you're a newbie, so here's an example:
#include <stdio.h>
void foo()
{
int a = 10;
static int sa = 10;
a += 5;
sa += 5;
printf("a = %d, sa = %d\n", a, sa);
}
int main()
{
int i;
for (i = 0; i < 10; ++i)
foo();
}
This prints:
a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60
This is useful for cases where a function needs to keep some state between invocations, and you don't want to use global variables. Beware, however, this feature should be used very sparingly - it makes your code not thread-safe and harder to understand.
(2) Is used widely as an "access control" feature. If you have a .c file implementing some functionality, it usually exposes only a few "public" functions to users. The rest of its functions should be made static
, so that the user won't be able to access them. This is encapsulation, a good practice.
Quoting Wikipedia:
In the C programming language, static
is used with global variables and
functions to set their scope to the
containing file. In local variables,
static is used to store the variable
in the statically allocated memory
instead of the automatically allocated
memory. While the language does not
dictate the implementation of either
type of memory, statically allocated
memory is typically reserved in data
segment of the program at compile
time, while the automatically
allocated memory is normally
implemented as a transient call stack.
And to answer your second question, it's not like in C#.
In C++, however, static
is also used to define class attributes (shared between all objects of the same class) and methods. In C there are no classes, so this feature is irrelevant.
Best Answer
It's actually just a normal data constructor that happens to be defined in the Prelude, which is the standard library that is imported automatically into every module.
What Maybe is, Structurally
The definition looks something like this:
That declaration defines a type,
Maybe a
, which is parameterized by a type variablea
, which just means that you can use it with any type in place ofa
.Constructing and Destructing
The type has two constructors,
Just a
andNothing
. When a type has multiple constructors, it means that a value of the type must have been constructed with just one of the possible constructors. For this type, a value was either constructed viaJust
orNothing
, there are no other (non-error) possibilities.Since
Nothing
has no parameter type, when it's used as a constructor it names a constant value that is a member of typeMaybe a
for all typesa
. But theJust
constructor does have a type parameter, which means that when used as a constructor it acts like a function from typea
toMaybe a
, i.e. it has the typea -> Maybe a
So, the constructors of a type build a value of that type; the other side of things is when you would like to use that value, and that is where pattern matching comes in to play. Unlike functions, constructors can be used in pattern binding expressions, and this is the way in which you can do case analysis of values that belong to types with more than one constructor.
In order to use a
Maybe a
value in a pattern match, you need to provide a pattern for each constructor, like so:In that case expression, the first pattern would match if the value was
Nothing
, and the second would match if the value was constructed withJust
. If the second one matches, it also binds the nameval
to the parameter that was passed to theJust
constructor when the value you're matching against was constructed.What Maybe Means
Maybe you were already familiar with how this worked; there's not really any magic to
Maybe
values, it's just a normal Haskell Algebraic Data Type (ADT). But it's used quite a bit because it effectively "lifts" or extends a type, such asInteger
from your example, into a new context in which it has an extra value (Nothing
) that represents a lack of value! The type system then requires that you check for that extra value before it will let you get at theInteger
that might be there. This prevents a remarkable number of bugs.Many languages today handle this sort of "no-value" value via NULL references. Tony Hoare, an eminent computer scientist (he invented Quicksort and is a Turing Award winner), owns up to this as his "billion dollar mistake". The Maybe type is not the only way to fix this, but it has proven to be an effective way to do it.
Maybe as a Functor
The idea of transforming one type to another one such that operations on the old type can also be transformed to work on the new type is the concept behind the Haskell type class called
Functor
, whichMaybe a
has a useful instance of.Functor
provides a method calledfmap
, which maps functions that range over values from the base type (such asInteger
) to functions that range over values from the lifted type (such asMaybe Integer
). A function transformed withfmap
to work on aMaybe
value works like this:So if you have a
Maybe Integer
valuem_x
and anInt -> Int
functionf
, you can dofmap f m_x
to apply the functionf
directly to theMaybe Integer
without worrying if it's actually got a value or not. In fact, you could apply a whole chain of liftedInteger -> Integer
functions toMaybe Integer
values and only have to worry about explicitly checking forNothing
once when you're finished.Maybe as a Monad
I'm not sure how familiar you are with the concept of a
Monad
yet, but you have at least usedIO a
before, and the type signatureIO a
looks remarkably similar toMaybe a
. AlthoughIO
is special in that it doesn't expose its constructors to you and can thus only be "run" by the Haskell runtime system, it's still also aFunctor
in addition to being aMonad
. In fact, there's an important sense in which aMonad
is just a special kind ofFunctor
with some extra features, but this isn't the place to get into that.Anyway, Monads like
IO
map types to new types that represent "computations that result in values" and you can lift functions intoMonad
types via a veryfmap
-like function calledliftM
that turns a regular function into a "computation that results in the value obtained by evaluating the function."You have probably guessed (if you have read this far) that
Maybe
is also aMonad
. It represents "computations that could fail to return a value". Just like with thefmap
example, this lets you do a whole bunch of computations without having to explicitly check for errors after each step. And in fact, the way theMonad
instance is constructed, a computation onMaybe
values stops as soon as aNothing
is encountered, so it's kind of like an immediate abort or a valueless return in the middle of a computation.You Could Have Written Maybe
Like I said before, there is nothing inherent to the
Maybe
type that is baked into the language syntax or runtime system. If Haskell didn't provide it by default, you could provide all of its functionality yourself! In fact, you could write it again yourself anyway, with different names, and get the same functionality.Hopefully you understand the
Maybe
type and its constructors now, but if there is still anything unclear, let me know!