Vim documentation: vital/Data/Optional

main help file
vital/Data/Optional.txt       Provide optional value

Maintainer: rhysd <lin90162@yahoo.co.jp>

==============================================================================
CONTENTS                                Vital.Data.Optional-contents

INTRODUCTION                    Vital.Data.Optional-introduction
TERM                            Vital.Data.Optional-term
INTERFACE                       Vital.Data.Optional-interface
  FUNCTIONS                       Vital.Data.Optional-functions



==============================================================================
INTRODUCTION                            Vital.Data.Optional-introduction

Vital.Data.Optional represents an optional value.  An optional value can be a
valid state (some) or invalid state (none).  This is like a Maybe in Haskell,
Option in Scala, std::optional in C++ and so on.  An optional value is usually
used as a result of process which may cause an error or as a cache of some
results.

Vim script doesn't have a way to treat a value which may be invalid.  Goal of
this library is to provide the way to treat an optional value in a good way.

The implementation of optional value is a List.  Empty list means an invalid
value and 1-element list means a valid value.  So, some functions for List
like empty() or map()  or filter() and so on are also available for an
optional value.


        let s:cache = O.none()

        function! GetData()
          if O.exists(s:cache)
            return O.get(s:cache)
          endif
          let data = DoHeavyProcess()
          let s:cache = O.some(data)
          return data
        endfunction


        function! GetInput()
                " Returns O.some(an input),
                " or returns O.none() if <C-c>
        endfunction

        function! GetCommandIfExists(arg)
                " Returns the detail if `arg` is an existent command
        endfunction

        " Show the command detail with an input
        O.map(GetInput(), { input ->
                \ O.map(GetCommandIfExists(input), { command ->
                        \ execute('echo ' . command)
                \ }
        \ })



==============================================================================
TERM                                    Vital.Data.Optional-term

{optional}                              Vital.Data.Optional-term-optional
        {optional} is an optional value.  It is a List of 0 or 1 element
        actually.

{none}                                  Vital.Data.Optional-term-none
        An optional value as invalid value.

{some}                                  Vital.Data.Optional-term-some
        An optional value which contains a value as valid value.



==============================================================================
INTERFACE                               Vital.Data.Optional-interface

------------------------------------------------------------------------------
FUNCTIONS                               Vital.Data.Optional-functions

none()                                  Vital.Data.Optional.none()
        Returns an optional value as invalid value ({none}).

some({value})                           Vital.Data.Optional.some()
        Returns an optional value which contains {value} as valid value
        ({some}).

new({value}, [{null}])                          Vital.Data.Optional.new()
        Same as some({value}) usually. If the given {value} is v:null, it
        returns none().

        Old vim does not have v:null unfortunately. In that case, provide
        any values at {null}. It's used instead of v:null.

        new(1) == some(1)
        new(0) == some(0)
        new(v:true) == some(v:true)
        new(v:null) == none()

        new('hello', 'null') == some(1)
        new('null', 'null') == none()
        new(v:null, 'null') == none() " v:null is always none


is_optional({value})                    Vital.Data.Optional.is_optional()
        Returns {value} is an optional value.  It actually checks that {value}
        is a List of 0 or 1 element.

empty({optional})                       Vital.Data.Optional.empty()
        Returns whether {optional} has an invalid value or not.  If {optional}
        has an invalid value, it returns 1.

exists({optional})                      Vital.Data.Optional.exists()
        Returns whether {optional} has a valid value or not.  If {optional}
        has a valid value, it returns 1.

set({optional}, {value})                Vital.Data.Optional.set()
        Updates {optional} with {value}.  The result is a valid optional value
        which contains {value}.

unset({optional}, {value})              Vital.Data.Optional.unset()
        Removes value which {optional} contains and makes {optional} invalid
        value.  If {optional} is already invalid, it does nothing.

get({optional})                         Vital.Data.Optional.get()
        Returns a contained value in {optional}.  If {optional} is an invalid
        value, it throws an exception.  You can catch it by the prefix
        "vital: Data.Optional: "

get_or({optional}, {alternative})       Vital.Data.Optional.get_or()
        Returns a contained value in {optional}.  If {optional} is an invalid
        value, it returns the {alternative}'s value instead.

        echo O.get_or(O.new(10), { -> -1 }) " 10
        echo O.get_or(O.none(),  { -> -1 }) " -1


get_unsafe({optional})                  Vital.Data.Optional.get_unsafe()
        Returns a contained value in {optional}.  If {optional} is an invalid
        value, the behavior is undefined.

has({optional}, {type})                 Vital.Data.Optional.has()
        Returns a type of {optional} is {type}.  It is expected that {type} is
        a result of type().  If {optional} is an invalid value, it always
        returns 0.

apply({func}, {args}...)                Vital.Data.Optional.apply()
        Applies {args} to {func} if all of {args} are valid optional value.
        Then it returns the result wrapped as valid optional value.
        If any of {args} is an invalid optional value, it doesn't call
        {func} and returns an invalid optional value.
        This is like Applicative in Haskell.
        When any of {args} is not an optional value, it throws an exception.

        For example, below is an example to update data in cache.


        let s:cache = O.none()

        ...

        function! Update(data, new_value)
          " Update a:data with a:new_value
          " This function doesn't consider the case when a:data is invalid
        endfunction

        ...

        " Update cache only if s:cache has a valid value.
        " Below invokes Update(O.get(cache), O.get(new_value)) if s:cache has
        " a valid value.
        call O.apply(function('Update'), s:cache, O.some(new_value))

map({optional}, {func})                 Vital.Data.Optional.map()
        Maps content of {optional} by predicate {func}. If content of
        {optional} is none, then {func} won't be invoked and none will be
        returned.

        function! Succ(x)
          return a:x + 1
        endfunction

        echo O.map(O.some(1), function('Succ')) " returns O.some(2)
        echo O.map(O.none(), function('Succ'))  " returns O.none()


bind({func}, {args}...)                 Vital.Data.Optional.bind()
        Applies {args} to {func} if all of {args} are valid optional value.
        Then it returns the result directly.  This assumes that {func}'s
        arguments doesn't care about an optional value and {func} returns an
        optional value.
        When any of {args} is not an optional value, it throws an exception.

        For example, division with safe way is like below:


        function! Div(a, b)
          if a:b == 0
            return O.none()
          endif
          return O.some(a:a / a:b)
        endfunction

        function! Sub(a, b)
            return O.some(a:a - a:b)
        endfunction

        let f = function('Div')
        let f2 = function('Sub')

        " Below invokes 10 / (2 - 2)
        let result = O.bind(f, O.some(10), O.bind(f2, O.some(2), O.some(2)))

        echo O.empty(result) " returns 1 because 10 / 0 occurs an error


flat_map({func}, {arg})         vital.Data.Optional.flat_map()
        Similar to vital.Data.Optional.bind() for the single argument.

flatten({optional}, [{limit}])          Vital.Data.Optional.flatten()
        Flattens a nested from optional values by default.

        echo O.flatten(O.some(O.some(10)))
                \ == O.some(10)
        echo O.flatten(O.some(O.none()))
                \ == O.none()
        echo O.flatten(O.none())
                \ == O.none()

        echo O.flatten(O.some(O.some(O.some(10))))
                \ == O.some(O.some(10))

        Or flattens nests with a specified limit.

        echo O.flatten(O.some(O.some(O.some(42))), 2)
                \ == O.some(42)

        Or fully flattens nests if 0 is specified for the limit.

        echo O.flatten(O.none(), 0)
                \ == O.none()

        echo O.flatten(O.some(O.some(O.some(10))), 0)
                \ == O.some(O.some(10))
        echo O.flatten(O.some(O.none()), 0)
                \ == O.none()


optional({optional}, {if_some}, {if_none})
                                        vital.Data.Optional.optional()
        Extracts {optional}. if the {optional} is a {some}, this maps {if_some}
        to the {some}. if the {optional} is a {none}, this returns {if_none}'s
        result.

        echo O.optional(O.some(10),
          \ { x -> string(x) },
          \ { -> "=)" }
        \ )
        " 10

        echo O.optional(O.none(),
          \ { x -> string(x) },
          \ { -> "=)" }
        \ )
        " =)


first({optional_list})                  Vital.Data.Optional.first()
        Finds a first {some}, or {none} if no {some} is found.

        echo O.first([O.none(), O.some('sugar'), O.some('sweat'), O.none()])
        " O.some('sugar')

        echo O.first([O.none()])
        " O.none()

        echo O.first([])
        " O.none()


last({optional_list})                   Vital.Data.Optional.last()
        Finds a last {some}, or {none} if no {some} is found.

        echo O.last([O.none(), O.some('sugar'), O.some('sweat'), O.none()])
        " O.some('sweat')

        echo O.last([O.none()])
        " O.none()

        echo O.last([])
        " O.none()


echo({optional}, [{highlight-group}])   Vital.Data.Optional.echo()
        Displays an optional value like :echo. The format is "Some(...)" or
        "None".  If {highlight-group} is specified, it displays a value
        with the highlight group.  When {optional} is not an optional value,
        it throws an exception.



==============================================================================
vim:tw=78:fo=tcq2mM:ts=8:ft=help:norl:noet

top - main help file - tag index