1 -- Copyright (C) 2008 Eric Kow
    2 --
    3 -- This program is free software; you can redistribute it and/or modify
    4 -- it under the terms of the GNU General Public License as published by
    5 -- the Free Software Foundation; either version 2, or (at your option)
    6 -- any later version.
    7 --
    8 -- This program is distributed in the hope that it will be useful,
    9 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
   10 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11 -- GNU General Public License for more details.
   12 --
   13 -- You should have received a copy of the GNU General Public License
   14 -- along with this program; see the file COPYING.  If not, write to
   15 -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   16 -- Boston, MA 02110-1301, USA.
   17 
   18 -- | This modules provides rudimentary natural language generation
   19 -- (NLG) utilities.  That is, generating natural language from a
   20 -- machine representation.  Initially, only English is supported at
   21 -- all.  Representations are implemented for:
   22 --
   23 --  * countable nouns (plurality); and
   24 --  * lists of clauses (foo, bar and/or baz).
   25 module English where
   26 
   27 import Data.List (isSuffixOf, intersperse)
   28 
   29 -- | > englishNum 0 (Noun "watch") "" == "watches"
   30 --   > englishNum 1 (Noun "watch") "" == "watch"
   31 --   > englishNum 2 (Noun "watch") "" == "watches"
   32 englishNum :: Countable n => Int -> n -> ShowS
   33 englishNum x = if x == 1 then singular else plural
   34 
   35 -- | Things that have a plural and singular spelling
   36 class Countable a where
   37   plural :: a -> ShowS
   38   singular :: a -> ShowS
   39 
   40 -- | This only distinguishes between nouns with a final -ch,
   41 --   and nouns which do not.
   42 --   More irregular nouns will just need to have their own type
   43 --
   44 --   > plural (Noun "batch") "" == "batches"
   45 --   > plural (Noun "bat")   "" == "bats"
   46 --   > plural (Noun "mouse") "" == "mouses" -- :-(
   47 newtype Noun = Noun String
   48 
   49 instance Countable Noun where
   50   -- more irregular nouns will just need to have their own type
   51   plural (Noun s) | "ch" `isSuffixOf` s = showString s .  showString "es"
   52   plural (Noun s) = showString s . showChar 's'
   53   singular (Noun s) =  showString s
   54 
   55 -- | > singular This (Noun "batch") "" == "this batch"
   56 --   > plural   This (Noun "batch") "" == "these batches"
   57 data This = This Noun
   58 
   59 instance Countable This where
   60   plural (This s)   = showString "these "  . plural s
   61   singular (This s) = showString "this "   . singular s
   62 
   63 -- | Given a list of things, combine them thusly:
   64 --
   65 --   > orClauses ["foo", "bar", "baz"] == "foo, bar or baz"
   66 andClauses, orClauses :: [String] -> String
   67 andClauses = intersperseLast ", " " and "
   68 orClauses  = intersperseLast ", " " or "
   69 
   70 -- | As 'intersperse', with a different separator for the last
   71 -- | interspersal.
   72 intersperseLast :: String -> String -> [String] -> String
   73 intersperseLast _ _ [] = ""
   74 intersperseLast _ _ [clause] = clause
   75 intersperseLast sep sepLast clauses =
   76     concat (intersperse sep $ init clauses) ++ sepLast ++ last clauses