packages feed

haskell-fsrs-7.1.0: src/FSRS/Types.hs

{-# LANGUAGE DerivingStrategies #-}

-- | The vocabulary shared by the whole package: the four grades a reviewer can
-- give a card, and the two-variable memory state FSRS tracks for it.
module FSRS.Types
  ( -- * Ratings
    Rating (..)
  , allRatings
  , ratingToInt
  , ratingFromInt

    -- * Memory state
  , MemoryState (..)

    -- * Type synonyms
  , Stability
  , Difficulty
  , Retrievability
  , Days
  ) where

-- | How well the card was recalled. The 'Enum' instance counts from @0@; the
-- FSRS papers and reference implementations number the ratings from @1@, which
-- is what 'ratingToInt' gives you.
data Rating
  = Again
  | Hard
  | Good
  | Easy
  deriving stock (Eq, Ord, Show, Read, Enum, Bounded)

-- | Every rating, from 'Again' to 'Easy'.
allRatings :: [Rating]
allRatings = [minBound .. maxBound]

-- | The rating as FSRS numbers it: @1@ for 'Again' through @4@ for 'Easy'.
ratingToInt :: Rating -> Int
ratingToInt r = fromEnum r + 1

-- | Inverse of 'ratingToInt'. 'Nothing' outside @1..4@.
ratingFromInt :: Int -> Maybe Rating
ratingFromInt n
  | n >= 1 && n <= 4 = Just (toEnum (n - 1))
  | otherwise = Nothing

-- | Memory half-life in days: the larger it is, the slower the card is
-- forgotten. In FSRS-7 stability is a genuine continuous quantity — sub-day
-- values are meaningful and are what the model uses for same-day reviews.
type Stability = Double

-- | How hard the card is for this reviewer, on a @[1, 10]@ scale.
type Difficulty = Double

-- | Probability of recall. FSRS-7 squeezes it into @[1e-5, 1 - 1e-5]@ so that
-- neither it nor its logarithm can saturate, so a card reviewed a moment ago
-- has a retrievability just short of @1@ rather than exactly @1@.
type Retrievability = Double

-- | A duration in days. Fractional values are meaningful throughout FSRS-7:
-- ten minutes is @10 / 1440@.
type Days = Double

-- | Everything FSRS remembers about a card.
--
-- FSRS-7 tracks /two/ memory traces rather than one. Both are stabilities in
-- the same units; they differ only in how fast they decay and in which weights
-- update them.
--
-- * 'memoryStability' is the slow trace — the durable memory, and the one a
--   scheduler turns into an interval.
-- * 'memoryStabilityFast' is the fast trace, which carries the short-lived
--   boost a review gives you. It is what makes same-day reviews behave
--   differently from spaced ones, and it replaces the elapsed-time blend
--   between a long- and a short-term update that earlier drafts of FSRS-7
--   used.
--
-- The forgetting curve is a mixture of the two, so 'retrievability' needs a
-- whole 'MemoryState' — including the difficulty, which in FSRS-7 shapes the
-- curve as well.
data MemoryState = MemoryState
  { memoryStability :: !Stability
  , memoryDifficulty :: !Difficulty
  , memoryStabilityFast :: !Stability
  }
  deriving stock (Eq, Ord, Show, Read)