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

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

# num_option -- optional numeric values
#
# This is a pseudo-numeric type that handles one additional
# value: nil.  Any operation on nil will result in nil for a
# numeric result or false for a boolean result.
#
public num_option(public T type : numeric)
  : choice T nil,
    monad T (num_option T)
is

  # Does this option contain a value of type T?
  #
  public exists => (num_option.this ? T   => true
                                    | nil => false)


  # short-hand postfix operator for 'exists'
  #
  public postfix ?? => exists


  # Does this option contain no value of type T?
  #
  public is_nil => !exists


  # short-hand postfix operator for 'is_nil'
  #
  public postfix !! => is_nil


  # value of an option that is known to contain a value
  #
  # this can only be called in cases where it is known for sure that this option
  # is not nil.  A runtime error will be created otherwise.
  #
  public val T
    pre
      safety: (num_option.this??)
  =>
    num_option.this ? v T => v
                    | nil => fuzion.std.panic "num_option.val called on nil"


  # unwrap value or get default
  #
  public get (default Lazy T) =>
    num_option.this ? v T => v
                    | nil => default


  # monadic operator
  public redef infix >>= (f T -> num_option T) => num_option.this.bind T f


  # monadic operator
  #
  # Same as non-generic >>=, but also maps to a different type B.
  #
  public bind(B type : numeric, f T -> num_option B) num_option B =>
    num_option.this ? v T => f v
                    | nil => nil


  # monadic operator for bool result, false for nil
  public infix >>? (f T -> bool) =>
    num_option.this ? v T => f v
                    | nil => false


  # basic prefix operations
  public prefix +? num_option T => num_option.this >>= v -> +v
  public prefix -? num_option T => num_option.this >>= v -> -?v

  # basic infix operations
  public infix +? (other num_option T) num_option T => num_option.this >>= v -> other >>= w -> v +? w
  public infix -? (other num_option T) num_option T => num_option.this >>= v -> other >>= w -> v -? w
  public infix *? (other num_option T) num_option T => num_option.this >>= v -> other >>= w -> v *? w
  public infix **?(other num_option T) num_option T => num_option.this >>= v -> other >>= w -> v **? w
  public infix /? (other num_option T) num_option T
    pre
      safety: !other.is_zero
  =>
    num_option.this >>= v -> other >>= w -> v / w

  public infix %? (other num_option T) num_option T
    pre
      safety: !other.is_zero
  =>
    num_option.this >>= v -> other >>= w -> v % w

  # comparison
  public infix ==? (other num_option T) bool => num_option.this >>? v -> other >>? w -> v = w
  public infix !=? (other num_option T) bool => num_option.this >>? v -> other >>? w -> v != w
  public infix <?  (other num_option T) bool => num_option.this >>? v -> other >>? w -> v <  w
  public infix <=? (other num_option T) bool => num_option.this >>? v -> other >>? w -> v ≤ w
  public infix >?  (other num_option T) bool => num_option.this >>? v -> other >>? w -> v >  w
  public infix >=? (other num_option T) bool => num_option.this >>? v -> other >>? w -> v ≥ w

  public is_zero => sign ==? 0

  public sign num_option i32 =>
    num_option.this ? v T => v.sign
                    | nil => nil

  public abs num_option T =>
    num_option.this >>= v -> if v.sign ≥ 0 v else -?v


  # converts num_option into a list of either a single element in case
  # num_option.this.exists or `nil`otherwise
  #
  public as_list list T
  =>
    num_option.this ? v T => v : nil
                    | nil => nil


  public redef as_string String =>
    num_option.this ? v T => v.as_string
                    | nil => "--no value--"


  # return function
  #
  public fixed type.return (a T) => num_option a


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