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

tuple.fz


# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation.  If not, see <https://www.gnu.org/licenses/>.


# -----------------------------------------------------------------------
#
#  Tokiwa Software GmbH, Germany
#
#  Source code of Fuzion standard library feature tuple
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# tuple -- feature used to define tuple types
#
# tuple types provide algebraic product types of all the generic arguments
# provided to tuple.
#
# The values within a tuple 'tuple A B C' can be accessed via the tuple's
# argument field 'values' followed by a selector referring to the generic
# argument's position: 'values.0', 'values.1' and 'values.2', respectively.
#
# Syntactic sugar of the Fuzion language permits an alternative notation
# to create values of tuple types as follows
#
#     t := (a, b, c, ... )
#
# is equivalent to
#
#     t := tuple a b c ...
#
# The actual generic types are inferred from the static types of the values
# 'a', 'b', 'c', ... the tuple is created from.
#
# Similarly, syntactic sugar for the destructuring of tuples can be used
# to access the values as in
#
#     (a, b, c, ...) := t
#
# In destructurings, we can denote values we are not interested in using
# '_' as in
#
#  (_, b) := ("first", "second")
#
# which will set 'b' to '"second"' and drop the first element of the tuple.
#
# As an example, say we want to identify a person by its name and its age,
# so we can define
#
#     a := ("Alice" , 11)
#     b := ("Bob"   , 22)
#     c := ("Claire", 33)
#
# Then, we could extract Bob's age using
#
#     (_, age) := b
#
# or Claire's name using
#
#     (name, _) := c
#
# Destructuring also works for general features, e.g.
#
#     point (x,y i32) is {}
#
#     p := point 3, 4
#     (px, py) := p       # will set px to 3 and py to 4
#
# and the destructured value can then be used to create a tuple
#
#     t := (px, py)       # will create tuple<i32,i32> instance
#
# however, tuples are not assignment compatible with general features even
# if they would destructure into the same types, i.e.,
#
#     u tuple i32 i32 = p  # will cause compile time error
#     q point = (7, 12)      # will cause compile time error
#
# The unit tuple '()' can be used as a shorthand to create the empty tuple
# 'tuple'.  The empty tuple can be destructured like any other tuple
# using
#
#     () := ()
#
# even though this has no effect.
#
# An instance of the single tuple 'tuple A' with sole element 'a' can not
# be created using syntactic sugar '(a)', this will produce the plain
# value of 'a' instead. However, destructuring of a single tuple is possible:
#
#     (a0) := tuple a
#
# which is equivalent to
#
#     a0 := a
#
# NYI: A single tuple 'tuple A' is currently not assignment compatible with
# type 'A', which would make handling of general tuples easier.
#
# tuples and destructuring can be used to swap two elements or create a
# permutation as in
#
#     (a, b) := (b, a)
#     (o, t, a, n) := (n, a, t, o)
#
# A tuple type with no actual generic arguments is isomorphic to 'unit', i.e, it
# is a type that has only one single value: '()'.
#
public tuple(public A type...,
             public values A...
             ) : property.orderable,
                 property.hashable
is


  # given a tuple of several effect instances
  # instate the in order of declaration and
  # run code in their context
  #
  #
  public infix !(# result type
                 R type,

                 # the code to execute with `e` instated.
                 code () -> R
  ) R
    # NYI: BUG: cannot constraint open type parameter yet: pre A : effect
  =>
    (values.typed_foldf (option (Functional R) nil) T,e,v->
      if T : effect
        match e
          nil => v.as_functional R
          f (Functional R) => f.and v
      else
        compile_time_panic).or_panic.call code


  # equality of two tuple `a` and `b` is defined only if all elements of the tuple
  # are equatable and all elements are equal.
  #
  # This will result in a runtime `panic` in case any element type is not equatable.
  #
  public redef type.equality(a, b tuple.this) bool
  =>
    a.values.typed_zip_and_fold b.values true tuple_zip_equals


  # A total order between two tuples `a` and `b` is defined by the total order of
  # their first elements. If the first `i` element pairs are equal, the order is defined by
  # the `i+1`th element pair.
  #
  # This will result in a runtime `panic` in case any element type is not orderable.
  #
  public redef type.lteq(a, b tuple.this) bool
  =>
    a.values.typed_zip_and_fold b.values 0 tuple_zip_lteq <= 0


  # create hash code for this tuple
  #
  # This should satisfy the following condition:
  #
  #   (T.equality a b) : (T.hash_code a = T.hash_code b)
  #
  # This will result in a compile-time `panic` in case any element type is not hashable.
  #
  public redef fixed type.hash_code(a tuple.this) u64 : container.typed_fold xxh_first a.values
  =>
    public redef apply(T type, h u64, v T) u64
    =>
      if T : property.hashable then
        xxh_next h (T.hash_code v)
      else
        compile_time_panic  # tuple is not hashable since one element type is not
    res


  # create a String from this instance.
  #
  public redef as_string String =>

    concat : container.typed_applicator String is
      public redef apply(V type, e String, v V) String => e="" ? $v : "$e, $v"

    "({values.typed_foldf "" concat})"


# helper feature for `tuple.type.equality` to compare elements in a tuple
#
tuple_zip_equals : container.typed_zipper bool is

  public redef apply(T type,
                     e bool,
                     v, w T) bool
  =>
    if T : property.equatable then
      e && v = w
    else
      compile_time_panic


# helper feature for `tuple.type.lteq` to compare elements in a tuple
#
tuple_zip_lteq : container.typed_zipper i32 is

  public redef apply(T type,
                     e i32,
                     v, w T) i32
  =>
    if T : property.orderable then
      if      e != 0 then  e
      else if v = w  then  0
      else if v < w  then -1
      else                +1
    else
      compile_time_panic

last changed: 2026-05-12