diff --git a/lib/Text/Takkyuu.hs b/lib/Text/Takkyuu.hs index f41b2e600b440782d5cdb6455c604d73868c934a..1efbcd6311dd5aec68591ac7847c2f4f5b82a532 100644 --- a/lib/Text/Takkyuu.hs +++ b/lib/Text/Takkyuu.hs @@ -1,9 +1,13 @@ module Text.Takkyuu ( - module Text.Takkyuu.Proto - , module Text.Takkyuu.French - , module Text.Takkyuu.Visualization + module Text.Takkyuu.Internal + , module Text.Takkyuu.IO + , module Text.Takkyuu.NLP + , module Text.Takkyuu.Protocol + , module Text.Takkyuu.Toolbox ) where -import Text.Takkyuu.Proto -import Text.Takkyuu.Visualization -import Text.Takkyuu.French +import Text.Takkyuu.Internal +import Text.Takkyuu.IO +import Text.Takkyuu.NLP +import Text.Takkyuu.Protocol +import Text.Takkyuu.Toolbox diff --git a/lib/Text/Takkyuu/Data.hs b/lib/Text/Takkyuu/Data.hs deleted file mode 100644 index 312b8958e1146e1f4daefe843d8382b9adaaf19b..0000000000000000000000000000000000000000 --- a/lib/Text/Takkyuu/Data.hs +++ /dev/null @@ -1,28 +0,0 @@ -{-# LANGUAGE NamedFieldPuns #-} -module Text.Takkyuu.Data ( - ) where - -data Move a = - Here a - | Back - -data Reply a = - Ok - | No - -data Round a = - Over Bool - | On { - move :: Move a - , next :: Reply a -> Round a - } - -fromList :: [a] -> Round a -fromList [] = Over True -fromList (a:as) = On { - move = Here a - , next - } - where - next Ok = fromList as - next _ = Over False diff --git a/lib/Text/Takkyuu/French.hs b/lib/Text/Takkyuu/French.hs index ba3ee0e62cb35507e9219ea76d37afeb8763d334..f843255ca0ebb49bbc7cb24383e5117b1e15f4aa 100644 --- a/lib/Text/Takkyuu/French.hs +++ b/lib/Text/Takkyuu/French.hs @@ -8,10 +8,11 @@ module Text.Takkyuu.French ( , syntax ) where +import Control.Monad.IO.Class (MonadIO(..)) import Data.Char (isSpace, toLower) import qualified Data.Map as Map (fromList) import qualified Data.Set as Set (fromList) -import Text.Takkyuu.Proto (Game, Labeled(..), Label(..), dico, labelizer, rules) +import Text.Takkyuu (Game, Label(..), Labeled(..), dico, labelizer, rules) data POS = Determiner @@ -38,10 +39,10 @@ data MorphoLabel = MorphoLabel { type LabeledWord = Labeled String MorphoLabel -french :: Game Char -> Game String -french = dico id isSpace $ Set.fromList ["le", "la", "chat", "lait", "marché"] +french:: MonadIO m => Game Char String m +french = dico toLower isSpace $ Set.fromList ["le", "la", "chat", "lait", "marché"] -tag :: Game String -> Game LabeledWord +tag :: Game String [LabeledWord] m tag = labelizer (fmap toLower) $ Map.fromList [ ("le", [MorphoLabel {pos = Determiner, gender = Masculine, number = Singular}]) @@ -59,7 +60,7 @@ tag = labelizer (fmap toLower) $ Map.fromList [ ]) ] -syntax :: Game LabeledWord -> Game [LabeledWord] +syntax :: Game LabeledWord [LabeledWord] m syntax = rules label . Set.fromList $ agreeSn <$> combinations where combinations = [(g, n) | g <- [Masculine, Feminine], n <- [Singular, Plural]] diff --git a/lib/Text/Takkyuu/IO.hs b/lib/Text/Takkyuu/IO.hs index a4ffbc7c1bb7474c44e1765339a78af23f6a065c..f9a91e46d0f08290d29b18c1c47cdfcac560092a 100644 --- a/lib/Text/Takkyuu/IO.hs +++ b/lib/Text/Takkyuu/IO.hs @@ -1,10 +1,35 @@ module Text.Takkyuu.IO ( - fromHandle + display + , debug + , fromHandle , toHandle ) where -import Text.Takkyuu.Data (Round(..)) +import Control.Monad.IO.Class (MonadIO(..)) +import System.IO (Handle) +import Text.Takkyuu.Internal (Game(..), Destination, Source) +import Text.Takkyuu.Protocol (Move(..), Reply(..)) +--import Text.Takkyuu.Toolbox (effect) -toHandle :: Handle -> Round a -> IO () -fromHandle :: Handle -> IO (Round a) +effect :: Monad m => m () -> Game a b m +effect m = M (m *> pure mempty) + +display :: MonadIO m => String -> Game a b m +display = effect . liftIO . putStrLn + +debug :: (MonadIO m, Show a) => String -> Game a a m +debug flag = Opponent traceMove + where + traceMove m = (Ok, (display $ flag ++ ":> " ++ show m) <> followMove m) + followMove (Finish exitStatus) = Finished exitStatus + followMove (Here a) = Player (Here a, traceReply) + followMove Back = Player (Back, traceReply) + traceReply r = (display $ flag ++ ":< " ++ show r) <> debug flag + + +toHandle :: MonadIO m => Handle -> Destination a m +toHandle = undefined + +fromHandle :: MonadIO m => Handle -> Source a m +fromHandle = undefined diff --git a/lib/Text/Takkyuu/Internal.hs b/lib/Text/Takkyuu/Internal.hs new file mode 100644 index 0000000000000000000000000000000000000000..7f40665e4f065f82faceb3c5b10590d80b894b89 --- /dev/null +++ b/lib/Text/Takkyuu/Internal.hs @@ -0,0 +1,68 @@ +{- LANGUAGE FlexibleContexts, DeriveGeneric #-} +module Text.Takkyuu.Internal ( + Game(..) + , Source + , Destination + , Match + , failed + , (<->) + , (//) + , play + ) where + +import Text.Takkyuu.Protocol (ExitStatus(..), Move(..), Reply(..)) + +data Game a b m = + Player (Move b, Reply b -> Game a b m) + | Opponent (Move a -> (Reply a, Game a b m)) + | M (m (Game a b m)) + | Finished ExitStatus + +type Source a m = Game () a m +type Destination a m = Game a () m +type Match m = Game () () m + +instance Functor m => Semigroup (Game a b m) where + Finished Success <> g = g + f@(Finished Failure) <> _ = f + M m <> g = M ((<> g) <$> m) + Player (m, next) <> g = Player (m, (<> g) . next) + Opponent next <> g = Opponent (fmap (<> g) . next) + +instance Functor m => Monoid (Game a b m) where + mempty = Finished Success + +failed :: Game a b m +failed = Finished Failure + +(//) :: Functor m => Game a b m -> Game a b m -> Game a b m +Finished Failure // g = g +f@(Finished Success) // _ = f +M m // g = M ((// g) <$> m) +Player (m, next) // g = Player (m, (// g) . next) +Opponent next // g = Opponent (fmap (// g) . next) + +(<->) :: Functor m => Game a b m -> Game b c m -> Game a c m +Player (moveB, next1) <-> Opponent next2 = + let (replyB, g) = next2 moveB in + next1 replyB <-> g +M m1 <-> g = M ((<-> g) <$> m1) +g <-> M m2 = M ((g <->) <$> m2) +Opponent next1 <-> g = + Opponent (fmap (<-> g) . next1) +g <-> Player (moveC, next2) = + Player (moveC, (g <->) . next2) +Finished Success <-> Finished Success = Finished Success +g@(Finished exitStatus1) <-> Opponent next2 = + case next2 (Finish exitStatus1) of + (_, Opponent _) -> Finished Failure + (Ok, g2) -> g <-> g2 + _ -> Finished Failure +Player (Finish exitStatus1, _) <-> Finished Success = Finished exitStatus1 +_ <-> Finished _ = Finished Failure + +play :: Monad m => Game () () m -> m ExitStatus +play (Finished exitStatus) = pure exitStatus +play (M m) = m >>= play +play (Player (_, next)) = play $ next Ok +play (Opponent next) = play . snd . next $ Here () diff --git a/lib/Text/Takkyuu/NLP.hs b/lib/Text/Takkyuu/NLP.hs new file mode 100644 index 0000000000000000000000000000000000000000..610ec6bbfdd2ccec19446983c8a3acb7f13a20a5 --- /dev/null +++ b/lib/Text/Takkyuu/NLP.hs @@ -0,0 +1,72 @@ +module Text.Takkyuu.NLP ( + Label(..) + , Labeled(..) + , dico + , fuzz + , labelizer + , rules + ) where + +import Control.Monad.IO.Class (MonadIO(..)) +import Data.Map (Map, findWithDefault, insertWith) +import qualified Data.Map as Map (empty) +import Data.Set (Set) +import qualified Data.Set as Set (empty, fromList, member) +import Text.Takkyuu.Internal (Game(..), (//)) +import Text.Takkyuu.Protocol (ExitStatus(..), Move(..), Reply(..)) +import Text.Takkyuu.Toolbox (accept, here, oneOf) + +fuzz :: (Eq a, Ord a, MonadIO m) => Set [a] -> Game a a m +--fuzz :: (Eq a, Ord a, Functor m) => Set [a] -> Game a a m +fuzz classes = accept expand + where + expand a = oneOf (a:findWithDefault [] a equiv) + equiv = foldl (\tmp0 l -> + foldl (\tmp1 x -> + insertWith (++) x (filter (/= x) l) tmp1 + ) tmp0 l + ) Map.empty classes + +dico :: (MonadIO m, Eq k, Ord k) => (a -> k) -> (k -> Bool) -> Set [k] -> Game a [a] m +--dico :: (Functor m, Eq k, Ord k) => (a -> k) -> (k -> Bool) -> Set [k] -> Game a [a] m +--dico :: (Eq k, Ord k) => (a -> k) -> (k -> Bool) -> Set [k] -> Game a [a] (StateT [String] IO) +dico projector _ vocabulary = start + where + start = Opponent $ continueFrom ([], [vocabulary]) + continueFrom currentState@(stack, candidatesStack) (Here a) = + let projection = projector a in + let newCandidates = foldMap (startingWith projection) (candidatesStack !! 0) in + case (length newCandidates, Set.member [] newCandidates) of + (0, _) -> (No, Opponent $ continueFrom currentState) + (1, True) -> (Ok, (here (reverse $ a:stack) <> start)) + (_, True) -> (Ok, (here (reverse $ a:stack) <> start) // Opponent (continueFrom (a:stack, newCandidates:candidatesStack))) + --(1, False) -> (Predict , …) + (_, False) -> (Ok, Opponent $ continueFrom (a:stack, newCandidates:candidatesStack)) + continueFrom (_:stack, _:candidatesStack) Back = + (Ok, Opponent $ continueFrom (stack, candidatesStack)) + continueFrom (_, _) Back = (No, Finished Failure) + continueFrom ([], _) (Finish Success) = + (Ok, Finished Success) + continueFrom (stack, _) (Finish Success) = + (Ok, here (reverse stack)) + continueFrom (_, _) (Finish Failure) = (Ok, Finished Failure) + startingWith e (x:xs) = Set.fromList $ if e == x then [xs] else [] + startingWith _ [] = Set.empty + +data Label l = + Unknown + | Label l + deriving (Eq, Ord, Show) + + +data Labeled a l = Labeled { + item :: a + , label :: Label l + } + deriving (Eq, Ord, Show) + +labelizer :: (a -> k) -> Map a [b] -> Game a [Labeled a b] m +labelizer = undefined + +rules :: (a -> k) -> Set [k] -> Game a [a] m +rules = undefined diff --git a/lib/Text/Takkyuu/Proto.hs b/lib/Text/Takkyuu/Proto.hs deleted file mode 100644 index 79e90be645a9231421d18c18c1f6efffe1b13394..0000000000000000000000000000000000000000 --- a/lib/Text/Takkyuu/Proto.hs +++ /dev/null @@ -1,171 +0,0 @@ -{-# LANGUAGE NamedFieldPuns #-} -module Text.Takkyuu.Proto ( - Game(..) - , Label(..) - , Labeled(..) - , game - , oneOf - , fuzz - , keep - , ignore - , dico - , labelizer - , fixTypo - , rules - ) where - -import Control.Applicative (Alternative(..)) -import Data.Map (Map, findWithDefault, insertWith) -import qualified Data.Map as Map (empty, lookup) -import Data.Set as Set (Set) -import qualified Data.Set as Set (empty, fromList, member) - -data Game a = - Over Bool - | On { - token :: a - , next :: Game a - , fallback :: Game a - } - deriving Show - -instance Semigroup (Game a) where - Over True <> g = g - o@(Over False) <> _ = o - o <> g = o { - next = next o <> g - , fallback = fallback o <> g - } - -instance Monoid (Game a) where - mempty = Over True - -instance Functor Game where - fmap _ (Over b) = Over b - fmap f o = o { - token = f $ token o - , next = f <$> next o - , fallback = f <$> fallback o - } - -instance Applicative Game where - pure token = On { - token - , next = mempty - , fallback = empty - } - - Over b <*> _ = Over b - _ <*> Over b = Over b - f <*> g = On { - token = (token f) (token g) - , next = (next f) <*> (next g) - , fallback = (fallback f) <*> (fallback g) - } - -instance Alternative Game where - empty = Over False - Over False <|> g = g - o@(Over True) <|> _ = o - o <|> g = o { - fallback = fallback o <|> g - } - -instance Monad Game where - Over b >>= _ = Over b - o >>= f = (f (token o) <> ((next o) >>= f)) <|> ((fallback o) >>= f) - -game :: [a] -> Game a -game = foldr ((<>) . pure) mempty - -oneOf :: [a] -> Game a -oneOf = foldr ((<|>) . pure) empty - -fuzz :: (Eq a, Ord a) => Set [a] -> Game a -> Game a -fuzz classes = (>>= alternatives) - where - alternatives t = oneOf (findWithDefault [t] t equiv) - equiv = foldl (\tmp0 l -> - foldl (\tmp1 x -> - insertWith (++) x l tmp1 - ) tmp0 l - ) Map.empty classes - -data Label l = - Unknown - | Label l - deriving (Eq, Ord, Show) - -data Labeled a l = Labeled { - item :: a - , label :: Label l - } - deriving (Eq, Ord, Show) - - -keep :: (a -> Bool) -> Game a -> Game a -keep predicate = (>>= select) - where - select a - | predicate a = pure a - | otherwise = Over False - -ignore :: (a -> Bool) -> Game a -> Game a -ignore p = keep (not . p) - -dico :: (Eq k, Ord k) => (a -> k) -> (k -> Bool) -> Set [k] -> Game a -> Game [a] -dico projector isBlank vocabulary = continueFrom start - where - start = ([], vocabulary) - continueFrom (stack@(_:_), candidates) (Over True) - | not $ Set.member [] candidates = pure (reverse stack) - continueFrom _ (Over b) = Over b - continueFrom state@(stack, candidates) (On {token, next, fallback}) - | isBlank (projector token) = - ((if null stack || Set.member [] candidates then empty else pure (reverse stack)) - <> continueFrom start next) - <|> continueFrom state next - <|> continueFrom state fallback - | otherwise = - let projection = projector token in - let newCandidates = foldMap (startingWith projection) candidates in - case (null newCandidates, Set.member [] newCandidates) of - (True, _) -> continueFrom state fallback <|> continueFrom (token:stack, newCandidates) next - (_, False) -> continueFrom (token:stack, newCandidates) next - <|> continueFrom state fallback - _ -> (pure (reverse $ token:stack) <> continueFrom start next) - <|> continueFrom (token:stack, newCandidates) next - <|> continueFrom state fallback - startingWith e (x:xs) = Set.fromList $ if e == x then [xs] else [] - startingWith _ [] = Set.empty - -rules :: (Eq k, Ord k) => (a -> k) -> Set [k] -> Game a -> Game [a] -rules projector vocabulary = continueFrom start - where - start = ([], vocabulary) - continueFrom _ (Over b) = Over b - continueFrom state@(stack, candidates) (On {token, next, fallback}) = - let projection = projector token in - let newCandidates = foldMap (startingWith projection) candidates in - case (null newCandidates, Set.member [] newCandidates) of - (True, _) -> continueFrom state fallback - (_, False) -> continueFrom (token:stack, newCandidates) next - <|> continueFrom state fallback - _ -> (pure (reverse $ token:stack) <> continueFrom start next) - <|> continueFrom (token:stack, newCandidates) next - <|> continueFrom state fallback - startingWith e (x:xs) = Set.fromList $ if e == x then [xs] else [] - startingWith _ [] = Set.empty - -labelizer :: Ord k => (a -> k) -> (Map k [b]) -> Game a -> Game (Labeled a b) -labelizer projector labels = (>>= alternatives) - where - alternatives t = - let possibleLabels = Map.lookup (projector t) labels in - oneOf $ maybe [Labeled t Unknown] (Labeled t . Label <$>) possibleLabels - -fixTypo :: Game Char -> Game Char -fixTypo = fuzz $ Set.fromList [ - "bh" - , "il1" - ] diff --git a/lib/Text/Takkyuu/Protocol.hs b/lib/Text/Takkyuu/Protocol.hs new file mode 100644 index 0000000000000000000000000000000000000000..33f76026b79fe165b3f2f5f5e32b2fd75508b460 --- /dev/null +++ b/lib/Text/Takkyuu/Protocol.hs @@ -0,0 +1,38 @@ +{-# LANGUAGE FlexibleContexts, DeriveGeneric #-} + +module Text.Takkyuu.Protocol ( + ExitStatus(..) + , Move(..) + , Reply(..) + ) where + +import Data.Aeson (FromJSON(..), ToJSON(..)) +import GHC.Generics (Generic) + +data ExitStatus = + Success + | Failure + deriving (Show, Generic) + +instance FromJSON ExitStatus +instance ToJSON ExitStatus + +data Move a = + Here a + | Back + | Finish ExitStatus + deriving (Show, Generic) + +instance FromJSON a => FromJSON (Move a) +instance ToJSON a => ToJSON (Move a) + +data Reply a = +--data Reply = + Ok + | No +-- | Error +-- | Predict a + deriving (Show, Generic) + +instance FromJSON a => FromJSON (Reply a) +instance ToJSON a => ToJSON (Reply a) diff --git a/lib/Text/Takkyuu/Toolbox.hs b/lib/Text/Takkyuu/Toolbox.hs new file mode 100644 index 0000000000000000000000000000000000000000..23ee21f92492d0a5e90af7381058dd2c8dcb3388 --- /dev/null +++ b/lib/Text/Takkyuu/Toolbox.hs @@ -0,0 +1,83 @@ +{-# LANGUAGE FlexibleContexts, DeriveGeneric #-} +module Text.Takkyuu.Toolbox ( + here + , back + , effect + , fromEffect + , accept + , game + , oneOf + , wire + , sink + , runMonoid + , runMonoidT + , integrator + ) where + +import Control.Monad.IO.Class (MonadIO(..)) +import Control.Monad.State (MonadState(..), modify, State, StateT(..), runState, runStateT) +import Text.Takkyuu.Internal (Game(..), Destination, Match, (//), play) +import Text.Takkyuu.IO (display) +import Text.Takkyuu.Protocol (ExitStatus(..), Move(..), Reply(..)) + +--checkOk :: Reply b -> Game a b m +checkOk :: MonadIO m => Reply b -> Game a b m +checkOk Ok = display "ok" <> Finished Success +checkOk _ = display "no" <> Finished Failure + +here :: MonadIO m => b -> Game a b m +here a = Player (Here a, checkOk) + +back :: MonadIO m => Game a b m +back = Player (Back, checkOk) + +fromEffect :: Monad m => m c -> (c -> Game a b m) -> Game a b m +fromEffect m f = M (m >>= pure . f) + +effect :: Monad m => m () -> Game a b m +effect m = M (m *> pure mempty) + +accept :: MonadIO m => (a -> Game a b m) -> Game a b m +--accept :: Functor m => (a -> Game a b m) -> Game a b m +accept f = Opponent use + where + use (Here a) = (Ok, f a <> accept f) + use Back = (Ok, back <> accept f) + use (Finish exitStatus) = (Ok, Finished exitStatus) + +wire :: MonadIO m => Game a a m +--wire :: Functor m => Game a a m +wire = accept here +{- +wire = Opponent pass + where + pass (Finish exitStatus) = (Ok, Finished exitStatus) + pass m = +-} + +game :: (MonadIO m, Foldable f) => f b -> Game a b m +--game :: (Functor m, Foldable f) => f b -> Game a b m +game = foldr (\a s -> here a <> s) (Finished Success) + +oneOf :: (MonadIO m, Foldable f) => f b -> Game a b m +--oneOf :: (Functor m, Foldable f) => f b -> Game a b m +oneOf = foldr (\a s -> here a // s) (Finished Failure) + +integrator :: MonadState [a] m => Destination a m +integrator = Opponent record + where + record (Here a) = (Ok, M (modify (a:) *> pure integrator)) + record Back = (Ok, M (modify (drop 1) *> pure integrator)) + record (Finish exitStatus) = (Ok, Finished exitStatus) + +sink :: Destination a m +sink = Opponent ack + where + ack (Finish exitStatus) = (Ok, Finished exitStatus) + ack _ = (Ok, sink) + +runMonoid :: Monoid a => Match (State a) -> (ExitStatus, a) +runMonoid m = runState (play m) mempty + +runMonoidT :: (Monad m, Monoid a) => Match (StateT a m) -> m (ExitStatus, a) +runMonoidT m = runStateT (play m) mempty diff --git a/lib/Text/Takkyuu/Visualization.hs b/lib/Text/Takkyuu/Visualization.hs deleted file mode 100644 index edc3d10b55c619e15610d21c990bce301a506697..0000000000000000000000000000000000000000 --- a/lib/Text/Takkyuu/Visualization.hs +++ /dev/null @@ -1,33 +0,0 @@ -{-# LANGUAGE NamedFieldPuns #-} -module Text.Takkyuu.Visualization ( - dump - , explore - , render - ) where - -import Text.Takkyuu.Proto (Game(..)) -import Text.Printf (printf) - -data Tree a = - Leaf Bool - | Tree { - unTree :: [(a, Tree a)] - } - deriving Show - -explore :: Game a -> Tree a -explore (Over b) = Leaf b -explore (On {token, next, fallback}) = - case explore fallback of - Leaf _ -> Tree [(token, explore next)] - Tree l -> Tree $ (token, explore next):l - -dump :: Show a => Tree a -> String -dump = unlines . dumpAux - where - dumpAux (Leaf b) = [if b then "o" else "x"] - dumpAux (Tree l) = l >>= f - f (a, tree) = printf "%s — %s" (show a) <$> dumpAux tree - -render :: Show a => Game a -> IO () -render = putStr . dump . explore diff --git a/takkyuu.cabal b/takkyuu.cabal index fedc628203850f1a4902dbbccfffe2e91ee868a6..4ca46df44d5bce4343059948f90e9005e36ac850 100644 --- a/takkyuu.cabal +++ b/takkyuu.cabal @@ -19,12 +19,17 @@ extra-source-files: CHANGELOG.md library exposed-modules: Text.Takkyuu - other-modules: Text.Takkyuu.French - , Text.Takkyuu.Proto - , Text.Takkyuu.Visualization + , Text.Takkyuu.French + other-modules: Text.Takkyuu.Internal + , Text.Takkyuu.NLP + , Text.Takkyuu.Protocol + , Text.Takkyuu.Toolbox + , Text.Takkyuu.IO -- other-extensions: build-depends: base >=4.14 && <4.15 + , aeson >=1.5.6 && <1.5.7 , containers >=0.6 && <0.7 + , mtl >=2.2 && <2.3 hs-source-dirs: lib default-language: Haskell2010 ghc-options: -Wall @@ -39,7 +44,9 @@ executable takkyuu test-suite takkyuu-test default-language: Haskell2010 - type: exitcode-stdio-1.0 + type: detailed-0.9 hs-source-dirs: test - main-is: MyLibTest.hs + test-module: Test build-depends: base >=4.14 && <4.15 + , Cabal + , takkyuu diff --git a/test/MyLibTest.hs b/test/MyLibTest.hs deleted file mode 100644 index 3e2059e31f5127521b263b051fc7247772685e1a..0000000000000000000000000000000000000000 --- a/test/MyLibTest.hs +++ /dev/null @@ -1,4 +0,0 @@ -module Main (main) where - -main :: IO () -main = putStrLn "Test suite not yet implemented." diff --git a/test/Test.hs b/test/Test.hs new file mode 100644 index 0000000000000000000000000000000000000000..170253b61bdcabea80be4dab10bb225693393279 --- /dev/null +++ b/test/Test.hs @@ -0,0 +1,9 @@ +module Test ( + tests + ) where + +import Distribution.TestSuite +import Text.Takkyuu (oneOf) + +tests :: IO [Test] +tests = pure []