Fuzion Logo
flang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

Static vs. Dynamic Typing

Dynamic typing in languages such as Python of JavaScript has clear advantages for developers that would like to reduce the amount of (keyboard-) typing they want to do. On the other hand, only static typing enables the definition of lear semantics together with enforcing that these semantics are respected. Furthermore, static typing enables compiler optimizations that typically go much further than what would be possible with dynamic typing.

Duck Typing

If it walks like a duck and it quacks like a duck, then it must be a duck is the concept behing the type system in popular languages such as Python, JavaScript, Perl, Ruby, etc. It is an important part in the convenience offered by these languages.

However, duck typing is very limited when it comes too verifying more complex aspects such as the guarantees given by a certain type. In Fuzion, the pre- and postconditions that specify when a feature within a type can be called and what this feature does are essential parts of that type. A different type can only be used if that different type gives the same guarantees. It is not sufficient if the different type just happens to define features that happen to have the same name.

Consequently, for safety-critical code, duck typing that does not take into account the contracts defined for a type is not applicable.

Type Inference

Powerful type inference can bring a lot of the benefits of dynamic typping to systems using static typing. Examples of Type inference in Fuzion are

Field type inference from assigned value:

  x := f()                               #       (1)

The type of field x is taken from the static type of the result of a call to f() in this example.

Function result type inferred from returned value:

  y => g(h(),i)                          #       (2)

The result type of routine h is taken from the static type of the result of a the expression g(h(),i) in this example.

Actual generic parameter inferred from actual argument:

  # create array of length n filled with x
  a(x T, i32 n) Array is
    result = Array(n, fun (_ i32) => x)

  hundredYs := a(y, 100)  # = [y,y,y, ... ]      (3)

Where T is inferred from the actual argument y passed to a. This, however, does not work for formal generic arguments that are not used in the signature as in

  stack (int capacity) is
    push(T x) is ...
    top T is ...

  s := stack(100)

Type inference could still be possible once a feature that uses the formal generic for its arguments is called, as in

  s.push("a string")                     #      (4)

but this seems to go too far

Feature Templates

The declaration of generic features could be cumbersome as in this example of times2 that requires an argument of a type that profides in infix * operator:

  times2(x T: numeric) => x * 2

  six   := times2(3)
  tau   := times2(3.14)
  two_i := times2(complex(0,1))

The problems with this is the use of a generic constraint that adds additional (keyboard) typing. Furthermore, the generic constraint in this case restricts the allowed types more than needed, so we cannot write

  byebye := times2("bye")

It would be convenient to be able to write

  times2(x) => x * 2

  six    := times2(3)                    #      (5)
  tau    := times2(3.14)
  two_i  := times2(complex(0,1))
  byebye := times2("bye")

and to have the language figure out the actual types that are used here.

Templates and modules

The use of templates with arguement types inferred from actual arguments is is ok as long as the declaration of times2 and its uses are local to one another (in the same file or compilation unit), since then the requirements on the types as given in the implementation are also local to one another.

However, it would not be desirable for a compilation unit (module) to use this kind of type inference for features that are visible to other compilation units since this would mean that a change in the implemtation that should not be visible outside of the module as in

  times2(x) => x + x

would result in a change in the requirement on the type of the argument passed to this feature. Consequently, features exported from modules should not be allowed to use type inference for their arguments, but instead have to define proper types.

Templates and implementation

In a statically typed language, we want to avoid the overhead of dynamic typing. This can be achieved for templates by duplicating code for a template for each call site where it is used, or at least once for every type signature it is used with. So a template actually defines a whole set of actual features.

Templates and redefinition

Should it be possible to redefine a template? If this was allowed, then a call site for a template would create not only an instance of the template, but one for every redefinition as well.

I could imagine that there are cases where this is useful, but I am unsure if this justifies the added comlexity in the front-end.

Typing in Fuzion

Fuzion uses static typing with heavy use of type inference such as

however, I consider the following as going too far for now: