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

outcome.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 outcome
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# outcome -- result type for functions that may return an error
#
# outcome is a choice type that represents the result of a routine that
# may either produce something useful or fail producing an error condition.
#
# Several error conditions are needed if there are several very different
# reasons for an operation to fail, e.g.
#
#     get_data (u User, t Type) outcome data IO_Error Permission_Error is
#       if u.allowed_to_access T
#         (read_file t.file_name)?
#       else
#         Permission_Error u, t
#
#     read_file t Type outcome data IO_Error is
#       [..]
#
# Note that 'outcome data IO_Error' is not assignment compatible with
# 'outcome data IO_Error Permission_Error', it has to be unwrapped first.
# This unwrapping, however, requires very little boilerplate, it is done
# using the '?' on the result of the call to 'read_file': This unwraps
# 'outcome data IO_Error' into 'IO_Error', which would be returned abruptly,
# and 'data', which would be returned normally. Both are assignment
# compatible to  'outcome data IO_Error Permission_Error', so everything
# is fine.
#
public outcome(public T type)
  : switch T error,
    property.equatable
is

  # Does this outcome contain an error
  #
  public is_error bool => !ok


  # error of an outcome that is known to contain an error
  #
  # This can only be called in cases where it is known for sure that this
  # outcome is an error.  A runtime error will be created otherwise.
  #
  public err error
    pre
      safety: outcome.this!!
  =>
    outcome.this ? T       => panic "outcome.err called on successful outcome. Enable `safety` to obtain a precondition failure for debugging."
                 | e error => e


  # converts outcome to a string
  #
  # returns the result of T.as_string for a successful outcome, or
  # "--$e--" for e error.
  #
  public redef as_string String =>
    outcome.this ? v T     => $v
                 | e error => "--$e--"


  # monadic operator
  #
  # This enables a very idiomatic way of error handling: assume you are trying
  # to read 42 bytes from a file into a string. Let open be a feature that takes
  # a file name and returns an outcome of some abstract file descriptor, let read
  # be a feature that takes an abstract file descriptor and a number of bytes to
  # read and returns an outcome of a byte (u8) array. We want to read 42 bytes
  # from a file called "example.txt" into a string, and we wrap this into an outcome,
  # for handling the case that an error occurs when opening the file (i.e. it does
  # not exist) or when reading the file (i.e. example.txt is not actually a file but
  # a directory). Using match expressions, this is very cumbersome:
  #
  #     # open example file
  #     match open "example.txt"
  #       fd i64 =>
  #         # read 42 bytes from example file
  #         match read fd 42
  #           content array u8 => String.from content
  #           re error => re
  #       oe error => oe
  #
  # The monadic operator turns this into:
  #
  #     (open "example.txt").bind (fd->read fd 42)
  #
  public fixed bind(B type, f T -> outcome B) outcome B =>
    outcome.this ? v T => f v
                 | e error => e


  # convert this to an outcome
  #
  # this is essentially a no-op.
  #
  public redef as_outcome outcome T
  =>
    outcome.this


  # equality implementation
  #
  # result is only true if both x and y are
  # of choice type T and the inner values are equal.
  #
  public redef fixed type.equality(x, y outcome T) bool
  =>
    if T : property.equatable
      x ? xa T =>
          y ? ya T => xa = ya
            | _  error => false
        | _  error => false
    else
      compile_time_panic


# outcome with 1 argument provides an shorthand to wrap a value into a
# outcome
#
# Using this enables to write
#
#     o := outcome x
#
# instead of
#
#     o outcome TypeOfX := x
#
public outcome(T type, o outcome T) outcome T => o

last changed: 2026-04-29