packages feed

hasklepias 0.6.1 → 0.7.0

raw patch · 6 files changed

+190/−183 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Hasklepias.Reexports: (=<<) :: Monad m => (a -> m b) -> m a -> m b
- Hasklepias.Reexports: infixr 1 =<<
- Hasklepias.Types.Feature: Feature :: Text -> b -> FeatureData d -> Feature b d
- Hasklepias.Types.Feature: FeatureData :: Either MissingReason d -> FeatureData d
- Hasklepias.Types.Feature: FeatureSpec :: Text -> b -> FeatureDefinition k d -> FeatureSpec b k d
- Hasklepias.Types.Feature: MkFeatureDef :: (input -> FeatureData d) -> FeatureDefinition input d
- Hasklepias.Types.Feature: class Defineable input
- Hasklepias.Types.Feature: define :: Defineable input => (input -> FeatureData d) -> FeatureDefinition input d
- Hasklepias.Types.Feature: eval :: Defineable input => FeatureDefinition input d -> input -> FeatureData d
- Hasklepias.Types.Feature: instance Hasklepias.Types.Feature.Defineable (Hasklepias.Types.Event.Events a)
- Hasklepias.Types.Feature: instance Hasklepias.Types.Feature.Defineable (Hasklepias.Types.Feature.FeatureData e, Hasklepias.Types.Event.Events a)
- Hasklepias.Types.Feature: instance Hasklepias.Types.Feature.Defineable (Hasklepias.Types.Feature.FeatureData e, Hasklepias.Types.Feature.FeatureData f)
- Hasklepias.Types.Feature: instance Hasklepias.Types.Feature.Defineable (Hasklepias.Types.Feature.FeatureData e, Hasklepias.Types.Feature.FeatureData f, Hasklepias.Types.Feature.FeatureData g)
- Hasklepias.Types.Feature: maybeFeature :: MissingReason -> (a -> Maybe c) -> (c -> d) -> a -> FeatureData d
- Hasklepias.Types.Feature: newtype FeatureDefinition input d
+ Hasklepias.Reexports: (*>) :: Applicative f => f a -> f b -> f b
+ Hasklepias.Reexports: (<*) :: Applicative f => f a -> f b -> f a
+ Hasklepias.Reexports: (<*>) :: Applicative f => f (a -> b) -> f a -> f b
+ Hasklepias.Reexports: (>>) :: Monad m => m a -> m b -> m b
+ Hasklepias.Reexports: (>>=) :: Monad m => m a -> (a -> m b) -> m b
+ Hasklepias.Reexports: class Functor f => Applicative (f :: Type -> Type)
+ Hasklepias.Reexports: class Applicative m => Monad (m :: Type -> Type)
+ Hasklepias.Reexports: infixl 1 >>
+ Hasklepias.Reexports: liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
+ Hasklepias.Reexports: pure :: Applicative f => a -> f a
+ Hasklepias.Reexports: return :: Monad m => a -> m a
+ Hasklepias.Types.Feature: FD0 :: (e -> FeatureData d) -> FeatureDefinition f e d
+ Hasklepias.Types.Feature: FD1 :: (FeatureData e -> FeatureData d) -> FeatureDefinition f e d
+ Hasklepias.Types.Feature: FD2 :: (FeatureData f -> FeatureData e -> FeatureData d) -> FeatureDefinition f e d
+ Hasklepias.Types.Feature: MkFeature :: Text -> b -> FeatureData d -> Feature b d
+ Hasklepias.Types.Feature: MkFeatureData :: Either MissingReason d -> FeatureData d
+ Hasklepias.Types.Feature: MkFeatureSpec :: Text -> b -> FeatureDefinition f e d -> FeatureSpec b f e d
+ Hasklepias.Types.Feature: data FeatureDefinition f e d
+ Hasklepias.Types.Feature: define0 :: (e -> FeatureData d) -> FeatureDefinition * e d
+ Hasklepias.Types.Feature: define1 :: (e -> d) -> FeatureDefinition * e d
+ Hasklepias.Types.Feature: define2 :: (f -> e -> d) -> FeatureDefinition f e d
+ Hasklepias.Types.Feature: define2d :: (f -> e -> FeatureData d) -> FeatureDefinition f e d
+ Hasklepias.Types.Feature: eval0 :: FeatureDefinition * e d -> e -> FeatureData d
+ Hasklepias.Types.Feature: eval1 :: FeatureDefinition * e d -> FeatureData e -> FeatureData d
+ Hasklepias.Types.Feature: eval2 :: FeatureDefinition f e d -> FeatureData f -> FeatureData e -> FeatureData d
+ Hasklepias.Types.Feature: evalSpec0 :: Show b => FeatureSpec b * e d -> e -> Feature b d
+ Hasklepias.Types.Feature: evalSpec1 :: Show b => FeatureSpec b * e d -> Feature b e -> Feature b d
+ Hasklepias.Types.Feature: evalSpec2 :: Show b => FeatureSpec b f e d -> Feature b f -> Feature b e -> Feature b d
+ Hasklepias.Types.Feature: instance (GHC.Show.Show b, GHC.Show.Show d) => GHC.Show.Show (Hasklepias.Types.Feature.Feature d b)
+ Hasklepias.Types.Feature: instance GHC.Base.Applicative Hasklepias.Types.Feature.FeatureData
+ Hasklepias.Types.Feature: instance GHC.Base.Monad Hasklepias.Types.Feature.FeatureData
+ Hasklepias.Types.Feature: instance GHC.Show.Show b => GHC.Base.Functor (Hasklepias.Types.Feature.Feature b)
+ Hasklepias.Types.Feature: makeFeatureSpec :: Show b => Text -> b -> FeatureDefinition f e d -> FeatureSpec b f e d
- Hasklepias.Types.Feature: [getDefn] :: FeatureSpec b k d -> FeatureDefinition k d
+ Hasklepias.Types.Feature: [getDefn] :: FeatureSpec b f e d -> FeatureDefinition f e d
- Hasklepias.Types.Feature: [getSpecAttr] :: FeatureSpec b k d -> b
+ Hasklepias.Types.Feature: [getSpecAttr] :: FeatureSpec b f e d -> b
- Hasklepias.Types.Feature: [getSpecName] :: FeatureSpec b k d -> Text
+ Hasklepias.Types.Feature: [getSpecName] :: FeatureSpec b f e d -> Text
- Hasklepias.Types.Feature: data (Show b) => FeatureSpec b k d
+ Hasklepias.Types.Feature: data (Show b) => FeatureSpec b f e d

Files

ChangeLog.md view
@@ -1,12 +1,17 @@ # Changelog for hasklepias +## 0.7.0++* Modifies the way that `Feature`s are defined and evaluated. For one, the dependency between `Events` and `Feature`s is eliminated, thus decoupling the defining of Features from the input data type. There are now 4 functions for defining features:+`define0`, `define1`, `define2`, and `define2d`, and 3 functions for evaluating `eval0`, `eval1`, and `eval2` (and corresponding `evalSpec*`). These functions will hopefully be combined into a single interface at later time. See the function signatures for the types of functions passed to a `define*` function. Examples will be forthcoming as the API stabilizes.+ ## 0.6.1  * Adds `derving Eq` to `Feature` type.  ## 0.6.0 -* Adds `PolyKinds` extension to `Feature` module to enable poly-kind inputs to `FeatureDefinition`s. Adds a related `Defineable` typeclass with `define` and `eval` functions as a common interface for defining new definitions and evaluating them.+* Adds `PolyKinds` extension to `Feature` module to enable poly-kind inputs to `FeatureDefinition`s. Adds a related `Defineable` typeclass with `define` and `eval` functions as a common interface for defining new definitions and evaluating them.  * Removes `defineEF` and `applyEF` function (and other similar functions). The functionality is now handled by the `Defineable` class.  ## 0.5.1
examples/ExampleFeatures1.hs view
@@ -18,28 +18,20 @@ import ExampleEvents import Test.Hspec import Data.Bifunctor--+import Control.Applicative+import Control.Monad  {- Index is defined as the first occurrence of an Orca bite. -}-indexDef :: (Ord a) => FeatureDefinition (Events a) (Interval a)-indexDef = define-      (\es ->-            case firstConceptOccurrence ["wasBitByOrca"] es of-                  Nothing -> featureDataL (Other "No occurrence of Orca bite")-                  Just x  -> featureDataR (getInterval x)-      )---- equivalent...-indexDef' :: (Ord a) => FeatureDefinition (Events a) (Interval a)-indexDef' = define $-      maybeFeature-            (Other "No occurrence of Orca bite")-            (firstConceptOccurrence ["wasBitByOrca"])-             getInterval+indexDef :: (Ord a) => Events a -> FeatureData (Interval a)+indexDef events =+  case firstConceptOccurrence ["wasBitByOrca"] events of+        Nothing -> featureDataL (Other "No occurrence of Orca bite")+        Just x  -> featureDataR (getInterval x) +indexSpec :: (Ord a) => FeatureSpec Text (*) (Events a) (Interval a)+indexSpec = makeFeatureSpec "index" "" (define0 indexDef)  {-   The baseline interval is the interval (b - 60, b), where b is the begin of @@ -55,12 +47,12 @@ bline :: (IntervalSizeable a b) =>      Events a   -> FeatureData (Interval a)-bline = baseline . eval indexDef+bline = baseline . indexDef  flwup :: (IntervalSizeable a b) =>      Events a   -> FeatureData (Interval a)-flwup = fmap (beginerval 30 . begin) . eval indexDef+flwup = fmap (beginerval 30 . begin) . indexDef  {- Define enrolled as the indicator of whether all of the gaps between the union of @@ -68,98 +60,70 @@ -} enrolledDef :: (IntervalSizeable a b) =>       b-      -> FeatureDefinition (FeatureData (Interval a), Events a) Bool-enrolledDef allowableGap = define-   ( \(dat, es) ->-         fmap-         (\i -> es-            |> makeConceptsFilter ["enrollment"]-            |> combineIntervals-            |> gapsWithin i-            |> maybe False (all (< allowableGap) . durations)-          )-         dat-   )-+      -> Interval a -> Events a -> Bool+enrolledDef allowableGap i events =+    events+      |> makeConceptsFilter ["enrollment"]+      |> combineIntervals+      |> gapsWithin i+      |> maybe False (all (< allowableGap) . durations)  {- Define features that identify whether a subject as bit/struck by a duck and bit/struck by a macaw. -}--- makeHxDef :: (Ord a) =>---          [Text] ---       -> FeatureDefinition * (Interval a) a (Bool, Maybe (Interval a))--- makeHxDef cnpts = defineFEF Excluded---    ( \i es ->---       (isNotEmpty (f i es), lastMay $ intervals (f i es))---    )---    where f i x = makePairedFilter enclose i (`hasConcepts` cnpts) x- makeHxDef :: (Ord a) =>          [Text]-      -> FeatureDefinition (FeatureData (Interval a), Events a) (Bool, Maybe (Interval a))-makeHxDef cnpts = define-   ( \(dat, es) ->-         fmap-         (\i -> (isNotEmpty (f i es), lastMay $ intervals (f i es)) )-         dat-   )+      -> Interval a+      -> Events a+      -> (Bool, Maybe (Interval a))+makeHxDef cnpts i events =+   (isNotEmpty (f i events), lastMay $ intervals (f i events))    where f i x = makePairedFilter enclose i (`hasConcepts` cnpts) x  duckHxDef :: (Ord a) =>-          FeatureDefinition (FeatureData (Interval a), Events a) (Bool, Maybe (Interval a))+         Interval a+      -> Events a+      -> (Bool, Maybe (Interval a)) duckHxDef = makeHxDef ["wasBitByDuck", "wasStruckByDuck"]  macawHxDef :: (Ord a) =>-          FeatureDefinition (FeatureData (Interval a), Events a) (Bool, Maybe (Interval a))+         Interval a+      -> Events a+      -> (Bool, Maybe (Interval a)) macawHxDef = makeHxDef ["wasBitByMacaw", "wasStruckByMacaw"]  -- | Define an event that identifies whether the subject has two minor or one major --   surgery. twoMinorOrOneMajorDef :: (Ord a) =>-         FeatureDefinition (FeatureData (Interval a), Events a) Bool-twoMinorOrOneMajorDef = define-      ( \(dat, es) ->-            fmap-            (\i -> twoXOrOneY ["hadMinorSurgery"] ["hadMajorSurgery"] (filterEnclose i es))-            dat-      )+         Interval a -> Events a ->  Bool+twoMinorOrOneMajorDef i events =+    twoXOrOneY ["hadMinorSurgery"] ["hadMajorSurgery"] (filterEnclose i events)  -- | Time from end of baseline to end of most recent Antibiotics --   with 5 day grace period timeSinceLastAntibioticsDef :: (IntervalSizeable a b) =>-      FeatureDefinition (FeatureData (Interval a), Events a) (Maybe b)-      --    FeatureDefinition * (Interval a) a (Maybe b)-timeSinceLastAntibioticsDef = define-      ( \(dat, es) ->-           fmap-            (\i ->-                  ( lastMay                                  -- want the last one-                  . map (max 0 . diff (end i) . end)        -- distances between end of baseline and antibiotic intervals-                  . filterNotDisjoint i                     -- filter to intervals not disjoint from baseline interval-                  . combineIntervals                        -- combine overlapping intervals-                  . map (expandr 5)                         -- allow grace period-                  . makeConceptsFilter ["tookAntibiotics"]) -- filter to only antibiotics events -                  es-            )-            dat-       )+      Interval a+      -> Events a+      -> Maybe b+timeSinceLastAntibioticsDef i  =+      lastMay                                 -- want the last one+    . map (max 0 . diff (end i) . end)        -- distances between end of baseline and antibiotic intervals+    . filterNotDisjoint i                     -- filter to intervals not disjoint from baseline interval+    . combineIntervals                        -- combine overlapping intervals+    . map (expandr 5)                         -- allow grace period+    . makeConceptsFilter ["tookAntibiotics"]  -- filter to only antibiotics events   -- | Count of hospital events in a interval and duration of the last one countOfHospitalEventsDef :: (IntervalSizeable a b) =>-      FeatureDefinition (FeatureData (Interval a), Events a) (Int, Maybe b)-countOfHospitalEventsDef = define-      ( \(dat, es) ->-            fmap-            ( \i ->-                   ( (\x -> (length x, duration <$> lastMay x))-                  . filterNotDisjoint i                    -- filter to intervals not disjoint from interval-                  . combineIntervals                       -- combine overlapping intervals-                  . makeConceptsFilter ["wasHospitalized"])  -- filter to only antibiotics events-                  es-            )-            dat-      )+      Interval a +      -> Events a +      -> (Int, Maybe b)+countOfHospitalEventsDef i =+       (\x -> (length x, duration <$> lastMay x))+    . filterNotDisjoint i                    -- filter to intervals not disjoint from interval+    . combineIntervals                       -- combine overlapping intervals+    . makeConceptsFilter ["wasHospitalized"]  -- filter to only antibiotics events  -- | time of distcontinuation of antibiotics --   and time from start of follow up@@ -169,27 +133,25 @@ so = unionPredicates [startedBy, overlappedBy]  discontinuationDef :: (IntervalSizeable a b) =>-      FeatureDefinition (FeatureData (Interval a), Events a) (Maybe (a, b))-                  --     FeatureDefinition * (Interval a) a (Maybe (a, b))-discontinuationDef = define-      (\(dat, es) ->-            fmap-            (\i -> (\x -> Just (begin x       -- we want the begin of this interval -                        , diff (begin x) (begin i)))-                  =<< headMay                    -- if there are any gaps the first one is the first discontinuation-                  =<< gapsWithin i               -- find gaps to intervals clipped to i-                  =<< (nothingIfNone (so i)      -- if none of the intervals start or overlap -                                     -- the followup, then never started antibiotics-                  . combineIntervals          -- combine overlapping intervals-                  . map (expandr 5)           -- allow grace period-                  . makeConceptsFilter        -- filter to only antibiotics events-                        ["tookAntibiotics"]) es)-            dat-      )+      Interval a +      -> Events a+      -> Maybe (a, b)+discontinuationDef i events = +    (\x -> Just (begin x       -- we want the begin of this interval +          , diff (begin x) (begin i)))+    =<< headMay                    -- if there are any gaps the first one is the first discontinuation+    =<< gapsWithin i               -- find gaps to intervals clipped to i+    =<< (nothingIfNone (so i)      -- if none of the intervals start or overlap +                        -- the followup, then never started antibiotics+    . combineIntervals          -- combine overlapping intervals+    . map (expandr 5)           -- allow grace period+    . makeConceptsFilter        -- filter to only antibiotics events+          ["tookAntibiotics"])+    events  getUnitFeatures ::       Events Int-  -> (FeatureData (Interval Int)+  -> ( FeatureData (Interval Int)      , FeatureData Bool      , FeatureData (Bool, Maybe (Interval Int))      , FeatureData (Bool, Maybe (Interval Int))@@ -199,15 +161,16 @@      , FeatureData (Maybe (Int, Int))      ) getUnitFeatures x = (-    eval indexDef x-  , eval (enrolledDef 8) (bline x, x)-  , eval duckHxDef (bline x, x)-  , eval macawHxDef (bline x, x)-  , eval twoMinorOrOneMajorDef (bline x, x)-  , eval timeSinceLastAntibioticsDef (bline x, x)-  , eval countOfHospitalEventsDef (bline x, x)-  , eval discontinuationDef (flwup x, x)-  )+    indexDef x+  , liftA2 (enrolledDef 8) (bline x) evs+  , liftA2 duckHxDef  (bline x) evs+  , liftA2 macawHxDef (bline x) evs+  , liftA2 twoMinorOrOneMajorDef (bline x) evs+  , liftA2 timeSinceLastAntibioticsDef (bline x) evs+  , liftA2 countOfHospitalEventsDef (bline x) evs+  , liftA2 discontinuationDef (flwup x) evs+  ) where evs = pure x+  exampleFeatures1Spec :: Spec exampleFeatures1Spec = do
examples/ExampleFeatures3.hs view
@@ -20,31 +20,22 @@   examplePairComparison :: (IntervalSizeable a b) =>-    FeatureDefinition (FeatureData (Interval a), Events a) (Bool, Maybe a)-            --    FeatureDefinition * (Interval a) a (Bool, Maybe a)-examplePairComparison = define-    (\ (dat, es) ->-        fmap-         (\i -> -            es-            |> filterConcur i                   -- filter to concurring with followup interval    -            |> splitByConcepts ["c1"] ["c2"]    -- form a list of pairs where first element-            |> uncurry allPairs                 -- has "c1" events and second has "c2" events    -                                                -            |> filter                           -- filter this list of pairs to cases -                (\pr -> fst pr `concur`             -- where "c1" event concurs with +/- 3-                    expand 3 3 (snd pr) )           -- of any "c2" event -            |> fmap fst-            |> intervals                        -- get the intervals of any "c1" events-            |> (\x ->-                ( isNotEmpty x                  -- are there any?-                , fmap begin (lastMay x)))      -- if exists, keep the begin of the last "c1" interval-            )-        dat-    -    )--+    Interval a+    -> Events a+    -> (Bool, Maybe a)+examplePairComparison i es = +    es+    |> filterConcur i                   -- filter to concurring with followup interval    +    |> splitByConcepts ["c1"] ["c2"]    -- form a list of pairs where first element+    |> uncurry allPairs                 -- has "c1" events and second has "c2" events    +                                        +    |> filter                           -- filter this list of pairs to cases +        (\pr -> fst pr `concur`             -- where "c1" event concurs with +/- 3+            expand 3 3 (snd pr) )           -- of any "c2" event +    |> fmap fst+    |> (\x ->+        ( isNotEmpty x                  -- are there any?+        , fmap begin (lastMay x)))      -- if exists, keep the begin of the last "c1" interval  flwup :: FeatureData (Interval Int) flwup = featureDataR $ beginerval 50 0@@ -53,6 +44,6 @@ exampleFeatures3Spec = do      it "examplePairComparison"  $-        eval examplePairComparison (flwup, exampleEvents4) `shouldBe`+        liftA2 examplePairComparison flwup (pure exampleEvents4)+             `shouldBe`         featureDataR (True, Just 16)-
hasklepias.cabal view
@@ -1,6 +1,6 @@ cabal-version:  2.2 name:           hasklepias-version:        0.6.1+version:        0.7.0 description:    Please see the README on GitHub at <https://github.com/novisci/asclepias#readme> homepage:       https://github.com/novisci/asclepias/#readme bug-reports:    https://github.com/novisci/asclepias/issues
src/Hasklepias/Reexports.hs view
@@ -37,8 +37,8 @@ import safe GHC.Num                         ( Integer ) import safe GHC.Generics                    ( Generic ) import safe GHC.Show                        ( Show(..) )-import safe Control.Monad                   ( (=<<), Functor(fmap) )-import safe Control.Applicative             ( (<$>) )+import safe Control.Monad                   ( Functor(fmap), Monad(..) )+import safe Control.Applicative             ( (<$>), Applicative(..) ) import safe Data.Bool                       ( Bool(..)                                             , (&&), not, (||)                                             , bool
src/Hasklepias/Types/Feature.hs view
@@ -9,10 +9,11 @@  {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE PolyKinds #-} {-# LANGUAGE Safe #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleContexts #-} module Hasklepias.Types.Feature(     -- * Types       FeatureSpec(..)@@ -20,24 +21,33 @@     , FeatureData(..)     , MissingReason(..)     , FeatureDefinition(..)-    , Defineable(..)-    , maybeFeature+    , makeFeatureSpec     , featureDataR     , featureDataL+    , define0+    , define1+    , define2+    , define2d+    , eval0+    , eval1+    , eval2+    , evalSpec0+    , evalSpec1+    , evalSpec2 ) where -import GHC.Read                   ( Read )-import GHC.Show                   ( Show )-import GHC.Generics               ( Generic )-import Data.Either                ( Either(..) )-import Data.Eq                    ( Eq )-import Data.Functor               ( Functor(fmap) )-import Data.Function              ( ($), (.) )-import Data.Maybe                 ( Maybe(..), maybe )-import Data.Ord                   ( Ord )-import Data.Text                  ( Text )-import Hasklepias.Types.Event     ( Event, Events )-import IntervalAlgebra            ( Interval, Intervallic )+import safe GHC.Read                   ( Read )+import safe GHC.Show                   ( Show(show) )+import safe GHC.Generics               ( Generic, D )+import safe Control.Applicative        ( Applicative(..) )+import safe Control.Monad              ( Functor(..), Monad(..), join, liftM2)+import safe Data.Either                ( Either(..) )+import safe Data.Eq                    ( Eq )+import safe Data.Function              ( ($), (.) )+import safe Data.List             ( (++) )  +import safe Data.Maybe                 ( Maybe(..), maybe )+import safe Data.Ord                   ( Ord )+import safe Data.Text                  ( Text ) -- import safe Test.QuickCheck       ( Property )  {- | A 'FeatureSpec' contains all the information needed to derive a 'Feature':@@ -45,48 +55,65 @@       * its attributes       * the function needed to derive a feature (i.e. the 'FeatureDefinition') -}-data (Show b) => FeatureSpec b k d = FeatureSpec {+data (Show b) => FeatureSpec b f e d = MkFeatureSpec {         getSpecName :: Text       , getSpecAttr :: b-      , getDefn :: FeatureDefinition k d+      , getDefn :: FeatureDefinition f e d       -- To add in future: an optional list of properties to check       -- , getProp :: Maybe [Feature d -> Events a -> Property]      }  -- | TODO-makeFeatureSpec :: Show b => Text -> b -> FeatureDefinition k d ->-  FeatureSpec b k d-makeFeatureSpec = FeatureSpec+makeFeatureSpec :: Show b =>+     Text+  -> b+  -> FeatureDefinition f e d+  -> FeatureSpec b f e d+makeFeatureSpec = MkFeatureSpec  {- | A 'Feature' contains the following:       * a name       * its attributes       * 'FeatureData' -}-data (Show b) => Feature b d = Feature {+data (Show b) => Feature b d = MkFeature {         getName :: Text       , getAttr :: b       , getData :: FeatureData d       } deriving (Eq) +instance (Show b, Show d) => Show (Feature d b) where +    show x = "(" ++ show (getName x) ++ ": (" ++ show (getAttr x) ++ ") "  ++ show (getData x) ++ " )\n"++instance (Show b) => Functor (Feature b) where+  fmap f (MkFeature n a d) = MkFeature n a (fmap f d)+ {- | 'FeatureData' is @'Either' 'MissingReason' d@, where @d@ can be any type       of data derivable from 'Hasklepias.Event.Events'. -}-newtype FeatureData d = FeatureData { getFeatureData :: Either MissingReason d }+newtype FeatureData d = MkFeatureData { getFeatureData :: Either MissingReason d }   deriving (Generic, Show, Eq)  instance Functor FeatureData where-  fmap f (FeatureData x) = FeatureData (fmap f x)+  fmap f (MkFeatureData x) = MkFeatureData (fmap f x) +instance Applicative FeatureData where+  pure = featureDataR+  liftA2 f (MkFeatureData x) (MkFeatureData y) = MkFeatureData ( liftA2 f x y )++instance Monad FeatureData where+  return = MkFeatureData . return+  x >>= f = do x >>= f+ -- | Create the 'Right' side of 'FeatureData'. featureDataR :: d -> FeatureData d-featureDataR = FeatureData . Right+featureDataR = MkFeatureData . Right  -- | Create the 'Left' side of 'FeatureData'. featureDataL :: MissingReason -> FeatureData d-featureDataL = FeatureData . Left+featureDataL = MkFeatureData . Left --- | A 'Feature' may be missing for any number of reasons. +-- | 'FeatureData' may be missing for any number of reasons.  data MissingReason =     InsufficientData   | Excluded@@ -94,21 +121,42 @@   | Unknown   deriving (Eq, Read, Show, Generic) ++-- TODO: the code below should be generalized so that there is a single define/eval+--       interface. -- | A type to hold FeatureData definitions; i.e. functions that return ---  features.-newtype FeatureDefinition input d = MkFeatureDef (input -> FeatureData d)+--  features. +data FeatureDefinition f e d =+    FD0 (e -> FeatureData d)+  | FD1 (FeatureData e -> FeatureData d)+  | FD2 (FeatureData f -> FeatureData e -> FeatureData d) -class Defineable input where-  define :: (input -> FeatureData d) -> FeatureDefinition input d-  define = MkFeatureDef+define0 :: (e -> FeatureData d) -> FeatureDefinition * e d+define0 = FD0 -  eval :: FeatureDefinition input d -> input -> FeatureData d-  eval (MkFeatureDef def) x = def x+eval0 :: FeatureDefinition * e d -> e -> FeatureData d+eval0 (FD0 f) = f -instance Defineable (Events a) where-instance Defineable (FeatureData e, Events a) where-instance Defineable (FeatureData e, FeatureData f) where-instance Defineable (FeatureData e, FeatureData f, FeatureData g) where+evalSpec0 :: (Show b) => FeatureSpec b * e d -> e -> Feature b d+evalSpec0 (MkFeatureSpec nm attr def) y = MkFeature nm attr (eval0 def y) -maybeFeature :: MissingReason -> (a -> Maybe c) -> (c -> d) -> (a -> FeatureData d)-maybeFeature r f g x = maybe (featureDataL r) (featureDataR . g) (f x)+define1 :: (e -> d) -> FeatureDefinition * e d+define1 f = FD1 $ fmap f++eval1 :: FeatureDefinition * e d -> FeatureData e -> FeatureData d+eval1 (FD1 f) = f++evalSpec1 :: (Show b) => FeatureSpec b * e d -> Feature b e -> Feature b d+evalSpec1 (MkFeatureSpec nm attr def) y = MkFeature nm attr (eval1 def (getData y))++define2 :: (f -> e -> d) -> FeatureDefinition f e d+define2 f =  FD2 $ liftA2 f++define2d :: (f -> e -> FeatureData d) -> FeatureDefinition f e d +define2d f = FD2 (\x y -> join (liftM2 f x y))++eval2 :: FeatureDefinition f e d -> FeatureData f -> FeatureData e -> FeatureData d+eval2 (FD2 f) = f ++evalSpec2 :: (Show b) => FeatureSpec b f e d -> Feature b f -> Feature b e -> Feature b d+evalSpec2 (MkFeatureSpec nm attr def) y z = MkFeature nm attr (eval2 def (getData y) (getData z))