From ca689ae5e056292390d2f463f932ec7677adaeec Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 18 Nov 2011 01:24:43 +0100 Subject: [PATCH] WIP some musing about building an option monad --- src/lib/maybe.hpp | 122 +++++++++++++++++++++++ tests/lib/maybe-value-test.cpp | 176 +++++++++++++++++++++++++++++++++ 2 files changed, 298 insertions(+) create mode 100644 src/lib/maybe.hpp create mode 100644 tests/lib/maybe-value-test.cpp diff --git a/src/lib/maybe.hpp b/src/lib/maybe.hpp new file mode 100644 index 000000000..085160a92 --- /dev/null +++ b/src/lib/maybe.hpp @@ -0,0 +1,122 @@ +/* + MAYBE.hpp - dealing with optional values + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + This program 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; either version 2 of + the License, or (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + + +/** @file maybe.hpp + ** Support for representation of optional values. + ** This implements a concept ("option monad") known from functional programming, + ** allowing to express the fact of some value possibly be unavailable. Using this + ** approach allows to avoid the dangerous technique of (ab)using NULL pointers to + ** represent missing values. + ** + ** While a NULL pointer carries this special meaning just by convention, marking a + ** parameter or return value as optional states this fact first class, and enforces + ** the necessary "is available" check through the type system. Surprisingly, this + ** leads not only to more secure, but also much more compact code, as we're now + ** able to substitute a fallback just by a "or else use this" clause. + ** Basically, there are different ways to access the actual value + ** - access through implicit conversion raises an exception for missing values + ** - evaluation as boolean allows to check, if the value is available + ** - an alternative or fallback value may be attached. + ** + ** @todo WIP and rather brainstorming as of 2/10 + ** + ** @see backend::ThreadJob usage example + */ + + + +#ifndef LIB_MAYBE_H +#define LIB_MAYBE_H + +//#include "pre.hpp" +#include "lib/error.hpp" +//#include "lib/wrapper.hpp" +#include "lib/util.hpp" + +#include + + + +namespace lib { + + using util::isnil; + using std::string; + namespace error = lumiera::error; + + + namespace maybe { + + } + + + /** + * A value, which might be unavailable + * @throw error::State on any attempt to access a missing value + * without prior checking the availability + */ + template + class Maybe + { + VAL value_; + + public: + /** mark an invalid/failed result */ + Maybe () + { } + + /** standard case: valid result */ + Maybe (VAL const& value) + : value_(value) + { } + + + + bool + isValid() const + { + UNIMPLEMENTED ("check if optional value is available"); + } + + void + maybeThrow(Literal explanation =0) const + { + if (!isValid()) + throw error::State (explanation.empty()? "optional value not available" : string(explanation), + error::LUMIERA_ERROR_BOTTOM_VALUE); + } + + + VAL + get() const + { + maybeThrow(); + return value_; + } + }; + + + + + +} // namespace lib +#endif diff --git a/tests/lib/maybe-value-test.cpp b/tests/lib/maybe-value-test.cpp new file mode 100644 index 000000000..28e778269 --- /dev/null +++ b/tests/lib/maybe-value-test.cpp @@ -0,0 +1,176 @@ +/* + MaybeValue(Test) - considerations for dealing with optional values + + Copyright (C) Lumiera.org + 2011, Hermann Vosseler + + This program 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; either version 2 of + the License, or (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/maybe.hpp" +#include "lib/util.hpp" + +//#include +//#include + + +namespace lib { +namespace test{ + + namespace error = lumiera::error; + +// using util::isSameObject; +// using std::rand; + using util::isnil; + using error::LUMIERA_ERROR_BOTTOM_VALUE; + + + namespace { // test data and helpers... + + + uint INVOCATION_CNT(0); + + /** helper for testing delayed evaluation */ + template + class Delayed + { + VAL v_; + + public: + Delayed (VAL val) : v_(val) { } + + VAL + operator() () const + { + ++INVOCATION_CNT; + return v_; + } + }; + + template + inline Delayed + yield (VAL val) + { + + } + } + + + + /*************************************************************************************** + * @test Investigate various situations of using a Maybe value or option monad. + * @note this is a testbed for experiments for the time being 11/2011 + * + * @see lib::Maybe + * @see null-value-test.cpp + * @see util::AccessCasted + */ + class MaybeValue_test : public Test + { + + void + run (Arg) + { + show_basicOperations(); + show_delayedAccess(); + } + + + void + show_basicOperations() + { +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856 + Maybe one(1); + Maybe opt(5); + Maybe nil; + + CHECK (opt); CHECK (!isnil(opt)); + CHECK (!nil); CHECK ( isnil(nil)); + + // access the optional value + CHECK (1 == *one); + CHECK (5 == *opt); + + // can't access an bottom value + VERIFY_ERROR (BOTTOM_VALUE, *nil); + + // flatMap operation (apply a function) + CHECK (7 == *(opt >>= inc2)); + CHECK (9 == *(opt >>= inc2 >>= inc2)); + + // alternatives + CHECK (1 == *(one || opt)); + CHECK (5 == *(nil || opt)); + CHECK (1 == *(nil || one || opt)); + + CHECK (1 == one.get()); + CHECK (1 == one.getOrElse(9)); + CHECK (9 == nil.getOrElse(9)); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856 + } + + + void + show_delayedAccess() + { + INVOCATION_CNT = 0; + + Maybe nil; + Maybe two(2); +#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856 + Maybe later(yield(5)); + CHECK (0 == INVOCATION_CNT); + + CHECK (2 == *(two || later)); + CHECK (0 == INVOCATION_CNT); + + CHECK (5 == *(nil || later)); + CHECK (1 == INVOCATION_CNT); + + later.get(); + CHECK (2 == INVOCATION_CNT); + + CHECK (2 == two.getOrElse(later)); + CHECK (2 == INVOCATION_CNT); + + CHECK (5 == nil.getOrElse(later)); + CHECK (3 == INVOCATION_CNT); + + // obviously, this also works just with a function + CHECK (7 == nil.getOrElse(yield(7))); + CHECK (4 == INVOCATION_CNT); + + // stripping the delayed evaluation + Maybe some = later; + CHECK (5 == INVOCATION_CNT); + CHECK (5 == some); + + CHECK (5 == INVOCATION_CNT); +#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #856 + } + }; + + + /** Register this test class... */ + LAUNCHER (MaybeValue_test, "unit common"); + + +}} // namespace lib::test