-- | Data type for OSC datum.
module Sound.OSC.Datum where

import Data.Int {- base -}
import Data.List {- base -}
import Data.Maybe {- base -}
import Data.Word {- base -}
import Numeric {- base -}
import Text.Read {- base -}

import qualified Data.ByteString.Lazy as Lazy {- bytestring -}
import qualified Data.ByteString.Char8 as Char8 {- bytestring -}

import qualified Sound.OSC.Time as Time {- hosc -}

-- * Datum

-- | Type enumerating Datum categories.
type Datum_Type = Char

-- | Type for ASCII strings (strict 'Char'8 'Char8.ByteString').
type ASCII = Char8.ByteString

-- | Type-specialised 'Char8.pack'.
ascii :: String -> ASCII
ascii :: String -> ASCII
ascii = String -> ASCII
Char8.pack

-- | Type-specialised 'Char8.unpack'.
ascii_to_string :: ASCII -> String
ascii_to_string :: ASCII -> String
ascii_to_string = ASCII -> String
Char8.unpack

-- | Type for 'Word8' arrays, these are stored with an 'Int32' length prefix.
type BLOB = Lazy.ByteString

-- | Type-specialised 'Lazy.pack'.
blob_pack ::  [Word8] -> BLOB
blob_pack :: [Word8] -> BLOB
blob_pack = [Word8] -> BLOB
Lazy.pack

-- | Type-specialised 'Lazy.unpack'.
blob_unpack :: BLOB -> [Word8]
blob_unpack :: BLOB -> [Word8]
blob_unpack = BLOB -> [Word8]
Lazy.unpack

-- | Four-byte midi message: port-id, status-byte, data, data.
data MIDI = MIDI Word8 Word8 Word8 Word8
    deriving (MIDI -> MIDI -> Bool
(MIDI -> MIDI -> Bool) -> (MIDI -> MIDI -> Bool) -> Eq MIDI
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: MIDI -> MIDI -> Bool
$c/= :: MIDI -> MIDI -> Bool
== :: MIDI -> MIDI -> Bool
$c== :: MIDI -> MIDI -> Bool
Eq,Int -> MIDI -> ShowS
[MIDI] -> ShowS
MIDI -> String
(Int -> MIDI -> ShowS)
-> (MIDI -> String) -> ([MIDI] -> ShowS) -> Show MIDI
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [MIDI] -> ShowS
$cshowList :: [MIDI] -> ShowS
show :: MIDI -> String
$cshow :: MIDI -> String
showsPrec :: Int -> MIDI -> ShowS
$cshowsPrec :: Int -> MIDI -> ShowS
Show,ReadPrec [MIDI]
ReadPrec MIDI
Int -> ReadS MIDI
ReadS [MIDI]
(Int -> ReadS MIDI)
-> ReadS [MIDI] -> ReadPrec MIDI -> ReadPrec [MIDI] -> Read MIDI
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [MIDI]
$creadListPrec :: ReadPrec [MIDI]
readPrec :: ReadPrec MIDI
$creadPrec :: ReadPrec MIDI
readList :: ReadS [MIDI]
$creadList :: ReadS [MIDI]
readsPrec :: Int -> ReadS MIDI
$creadsPrec :: Int -> ReadS MIDI
Read)

-- | The basic elements of OSC messages.
data Datum = Int32 {Datum -> Int32
d_int32 :: Int32}
           | Int64 {Datum -> Int64
d_int64 :: Int64}
           | Float {Datum -> Float
d_float :: Float}
           | Double {Datum -> Double
d_double :: Double}
           | ASCII_String {Datum -> ASCII
d_ascii_string :: ASCII}
           | Blob {Datum -> BLOB
d_blob :: BLOB}
           | TimeStamp {Datum -> Double
d_timestamp :: Time.Time} -- ie. NTPr
           | Midi {Datum -> MIDI
d_midi :: MIDI}
             deriving (Datum -> Datum -> Bool
(Datum -> Datum -> Bool) -> (Datum -> Datum -> Bool) -> Eq Datum
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Datum -> Datum -> Bool
$c/= :: Datum -> Datum -> Bool
== :: Datum -> Datum -> Bool
$c== :: Datum -> Datum -> Bool
Eq,ReadPrec [Datum]
ReadPrec Datum
Int -> ReadS Datum
ReadS [Datum]
(Int -> ReadS Datum)
-> ReadS [Datum]
-> ReadPrec Datum
-> ReadPrec [Datum]
-> Read Datum
forall a.
(Int -> ReadS a)
-> ReadS [a] -> ReadPrec a -> ReadPrec [a] -> Read a
readListPrec :: ReadPrec [Datum]
$creadListPrec :: ReadPrec [Datum]
readPrec :: ReadPrec Datum
$creadPrec :: ReadPrec Datum
readList :: ReadS [Datum]
$creadList :: ReadS [Datum]
readsPrec :: Int -> ReadS Datum
$creadsPrec :: Int -> ReadS Datum
Read,Int -> Datum -> ShowS
[Datum] -> ShowS
Datum -> String
(Int -> Datum -> ShowS)
-> (Datum -> String) -> ([Datum] -> ShowS) -> Show Datum
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Datum] -> ShowS
$cshowList :: [Datum] -> ShowS
show :: Datum -> String
$cshow :: Datum -> String
showsPrec :: Int -> Datum -> ShowS
$cshowsPrec :: Int -> Datum -> ShowS
Show)

-- * Datum types

-- | List of required data types (tag,name).
osc_types_required :: [(Datum_Type,String)]
osc_types_required :: [(Datum_Type, String)]
osc_types_required =
    [('i',"Int32")
    ,('f',"Float")
    ,('s',"ASCII_String") -- ASCII
    ,('b',"ByteArray") -- Blob
    ]

-- | List of optional data types (tag,name).
osc_types_optional :: [(Datum_Type,String)]
osc_types_optional :: [(Datum_Type, String)]
osc_types_optional =
    [('h',"Int64")
    ,('t',"TimeStamp")
    ,('d',"Double")
    -- ,('S',"Symbol")
    -- ,('c',"ASCII_Character")
    -- ,('r',"RGBA")
    ,('m',"MIDI")
    -- ,('T',"True")
    -- ,('F',"False")
    -- ,('N',"Nil")
    -- ,('I',"Infinitum")
    -- ,('[',"Array_Begin")
    -- ,(']',"Array_End")
    ]

-- | List of all data types (tag,name).
osc_types :: [(Datum_Type,String)]
osc_types :: [(Datum_Type, String)]
osc_types = [(Datum_Type, String)]
osc_types_required [(Datum_Type, String)]
-> [(Datum_Type, String)] -> [(Datum_Type, String)]
forall a. [a] -> [a] -> [a]
++ [(Datum_Type, String)]
osc_types_optional

-- | Lookup name of type.
osc_type_name :: Datum_Type -> Maybe String
osc_type_name :: Datum_Type -> Maybe String
osc_type_name c :: Datum_Type
c = Datum_Type -> [(Datum_Type, String)] -> Maybe String
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup Datum_Type
c [(Datum_Type, String)]
osc_types

-- | Erroring variant.
osc_type_name_err :: Datum_Type -> String
osc_type_name_err :: Datum_Type -> String
osc_type_name_err = String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe (ShowS
forall a. HasCallStack => String -> a
error "osc_type_name") (Maybe String -> String)
-> (Datum_Type -> Maybe String) -> Datum_Type -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Datum_Type -> Maybe String
osc_type_name

-- | Single character identifier of an OSC datum.
datum_tag :: Datum -> Datum_Type
datum_tag :: Datum -> Datum_Type
datum_tag d :: Datum
d =
    case Datum
d of
      Int32 _ -> 'i'
      Int64 _ -> 'h'
      Float _ -> 'f'
      Double _ -> 'd'
      ASCII_String _ -> 's'
      Blob _ -> 'b'
      TimeStamp _ -> 't'
      Midi _ -> 'm'

-- | Type and name of 'Datum'.
datum_type_name :: Datum -> (Datum_Type,String)
datum_type_name :: Datum -> (Datum_Type, String)
datum_type_name d :: Datum
d = let c :: Datum_Type
c = Datum -> Datum_Type
datum_tag Datum
d in (Datum_Type
c,Datum_Type -> String
osc_type_name_err Datum_Type
c)

-- * Generalised element access

-- | 'Datum' as 'Integral' if Int32 or Int64.
--
-- > let d = [Int32 5,Int64 5,Float 5.5,Double 5.5]
-- > map datum_integral d == [Just (5::Int),Just 5,Nothing,Nothing]
datum_integral :: Integral i => Datum -> Maybe i
datum_integral :: Datum -> Maybe i
datum_integral d :: Datum
d =
    case Datum
d of
      Int32 x :: Int32
x -> i -> Maybe i
forall a. a -> Maybe a
Just (Int32 -> i
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
x)
      Int64 x :: Int64
x -> i -> Maybe i
forall a. a -> Maybe a
Just (Int64 -> i
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
x)
      _ -> Maybe i
forall a. Maybe a
Nothing

-- | 'Datum' as 'Floating' if Int32, Int64, Float, Double or TimeStamp.
--
-- > let d = [Int32 5,Int64 5,Float 5,Double 5,TimeStamp 5]
-- > mapMaybe datum_floating d == replicate 5 (5::Double)
datum_floating :: Floating n => Datum -> Maybe n
datum_floating :: Datum -> Maybe n
datum_floating d :: Datum
d =
    case Datum
d of
      Int32 n :: Int32
n -> n -> Maybe n
forall a. a -> Maybe a
Just (Int32 -> n
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
n)
      Int64 n :: Int64
n -> n -> Maybe n
forall a. a -> Maybe a
Just (Int64 -> n
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int64
n)
      Float n :: Float
n -> n -> Maybe n
forall a. a -> Maybe a
Just (Float -> n
forall a b. (Real a, Fractional b) => a -> b
realToFrac Float
n)
      Double n :: Double
n -> n -> Maybe n
forall a. a -> Maybe a
Just (Double -> n
forall a b. (Real a, Fractional b) => a -> b
realToFrac Double
n)
      TimeStamp n :: Double
n -> n -> Maybe n
forall a. a -> Maybe a
Just (Double -> n
forall a b. (Real a, Fractional b) => a -> b
realToFrac Double
n)
      _ -> Maybe n
forall a. Maybe a
Nothing

-- * Constructors

-- | Type generalised Int32.
--
-- > int32 (1::Int32) == int32 (1::Integer)
-- > d_int32 (int32 (maxBound::Int32)) == maxBound
-- > int32 (((2::Int) ^ (64::Int))::Int) == Int32 0
int32 :: Integral n => n -> Datum
int32 :: n -> Datum
int32 = Int32 -> Datum
Int32 (Int32 -> Datum) -> (n -> Int32) -> n -> Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> Int32
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | Type generalised Int64.
--
-- > int64 (1::Int32) == int64 (1::Integer)
-- > d_int64 (int64 (maxBound::Int64)) == maxBound
int64 :: Integral n => n -> Datum
int64 :: n -> Datum
int64 = Int64 -> Datum
Int64 (Int64 -> Datum) -> (n -> Int64) -> n -> Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral

-- | Type generalised Float.
--
-- > float (1::Int) == float (1::Double)
-- > floatRange (undefined::Float) == (-125,128)
-- > isInfinite (d_float (float (encodeFloat 1 256 :: Double))) == True
float :: Real n => n -> Datum
float :: n -> Datum
float = Float -> Datum
Float (Float -> Datum) -> (n -> Float) -> n -> Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> Float
forall a b. (Real a, Fractional b) => a -> b
realToFrac

-- | Type generalised Double.
--
-- > double (1::Int) == double (1::Double)
-- > double (encodeFloat 1 256 :: Double) == Double 1.157920892373162e77
double :: Real n => n -> Datum
double :: n -> Datum
double = Double -> Datum
Double (Double -> Datum) -> (n -> Double) -> n -> Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. n -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac

-- | 'ASCII_String' of 'Char8.pack'.
--
-- > string "string" == ASCII_String (Char8.pack "string")
string :: String -> Datum
string :: String -> Datum
string = ASCII -> Datum
ASCII_String (ASCII -> Datum) -> (String -> ASCII) -> String -> Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ASCII
Char8.pack

-- | Four-tuple variant of 'Midi' '.' 'MIDI'.
--
-- > midi (0,0,0,0) == Midi (MIDI 0 0 0 0)
midi :: (Word8,Word8,Word8,Word8) -> Datum
midi :: (Word8, Word8, Word8, Word8) -> Datum
midi (p :: Word8
p,q :: Word8
q,r :: Word8
r,s :: Word8
s) = MIDI -> Datum
Midi (Word8 -> Word8 -> Word8 -> Word8 -> MIDI
MIDI Word8
p Word8
q Word8
r Word8
s)

-- * Descriptor

-- | Message argument types are given by a descriptor.
--
-- > descriptor [Int32 1,Float 1,string "1"] == ascii ",ifs"
descriptor :: [Datum] -> ASCII
descriptor :: [Datum] -> ASCII
descriptor l :: [Datum]
l = String -> ASCII
Char8.pack (',' Datum_Type -> ShowS
forall a. a -> [a] -> [a]
: (Datum -> Datum_Type) -> [Datum] -> String
forall a b. (a -> b) -> [a] -> [b]
map Datum -> Datum_Type
datum_tag [Datum]
l)

-- | Descriptor tags are @comma@ prefixed.
descriptor_tags :: ASCII -> ASCII
descriptor_tags :: ASCII -> ASCII
descriptor_tags = Int -> ASCII -> ASCII
Char8.drop 1

-- * Pretty printing

-- | Perhaps a precision value for floating point numbers.
type FP_Precision = Maybe Int

-- | Variant of 'showFFloat' that deletes trailing zeros.
--
-- > map (floatPP (Just 4)) [1,pi] == ["1.0","3.1416"]
floatPP :: RealFloat n => Maybe Int -> n -> String
floatPP :: Maybe Int -> n -> String
floatPP p :: Maybe Int
p n :: n
n =
    let s :: String
s = Maybe Int -> n -> ShowS
forall a. RealFloat a => Maybe Int -> a -> ShowS
showFFloat Maybe Int
p n
n ""
        s' :: String
s' = (Datum_Type -> Bool) -> ShowS
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Datum_Type -> Datum_Type -> Bool
forall a. Eq a => a -> a -> Bool
== '0') (ShowS
forall a. [a] -> [a]
reverse String
s)
    in case String
s' of
         '.':_ -> ShowS
forall a. [a] -> [a]
reverse ('0' Datum_Type -> ShowS
forall a. a -> [a] -> [a]
: String
s')
         _ -> ShowS
forall a. [a] -> [a]
reverse String
s'

-- | Pretty printer for 'Time'.
--
-- > timePP (Just 4) (1/3) == "0.3333"
timePP :: FP_Precision -> Time.Time -> String
timePP :: Maybe Int -> Double -> String
timePP = Maybe Int -> Double -> String
forall n. RealFloat n => Maybe Int -> n -> String
floatPP

-- | Pretty printer for vectors.
--
-- > vecPP [1::Int,2,3] == "<1,2,3>"
vecPP :: Show a => [a] -> String
vecPP :: [a] -> String
vecPP v :: [a]
v = '<' Datum_Type -> ShowS
forall a. a -> [a] -> [a]
: String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate "," ((a -> String) -> [a] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map a -> String
forall a. Show a => a -> String
show [a]
v) String -> ShowS
forall a. [a] -> [a] -> [a]
++ ">"

{- | Pretty printer for 'Datum'.

> let d = [Int32 1,Float 1.2,string "str",midi (0,0x90,0x40,0x60)]
> in map (datumPP (Just 5)) d ==  ["1","1.2","\"str\"","<0,144,64,96>"]

-}
datumPP :: FP_Precision -> Datum -> String
datumPP :: Maybe Int -> Datum -> String
datumPP p :: Maybe Int
p d :: Datum
d =
    case Datum
d of
      Int32 n :: Int32
n -> Int32 -> String
forall a. Show a => a -> String
show Int32
n
      Int64 n :: Int64
n -> Int64 -> String
forall a. Show a => a -> String
show Int64
n
      Float n :: Float
n -> Maybe Int -> Float -> String
forall n. RealFloat n => Maybe Int -> n -> String
floatPP Maybe Int
p Float
n
      Double n :: Double
n -> Maybe Int -> Double -> String
forall n. RealFloat n => Maybe Int -> n -> String
floatPP Maybe Int
p Double
n
      ASCII_String s :: ASCII
s -> ShowS
forall a. Show a => a -> String
show (ASCII -> String
Char8.unpack ASCII
s)
      Blob s :: BLOB
s -> BLOB -> String
forall a. Show a => a -> String
show BLOB
s
      TimeStamp t :: Double
t -> Maybe Int -> Double -> String
timePP Maybe Int
p Double
t
      Midi (MIDI b1 :: Word8
b1 b2 :: Word8
b2 b3 :: Word8
b3 b4 :: Word8
b4) -> [Word8] -> String
forall a. Show a => [a] -> String
vecPP [Word8
b1,Word8
b2,Word8
b3,Word8
b4]

-- | Variant of 'datumPP' that appends the 'datum_type_name'.
datum_pp_typed :: FP_Precision -> Datum -> String
datum_pp_typed :: Maybe Int -> Datum -> String
datum_pp_typed fp :: Maybe Int
fp d :: Datum
d = Maybe Int -> Datum -> String
datumPP Maybe Int
fp Datum
d String -> ShowS
forall a. [a] -> [a] -> [a]
++ ":" String -> ShowS
forall a. [a] -> [a] -> [a]
++ (Datum_Type, String) -> String
forall a b. (a, b) -> b
snd (Datum -> (Datum_Type, String)
datum_type_name Datum
d)

-- * Parser

-- | Given 'Datum_Type' attempt to parse 'Datum' at 'String'.
--
-- > parse_datum 'i' "42" == Just (Int32 42)
-- > parse_datum 'h' "42" == Just (Int64 42)
-- > parse_datum 'f' "3.14159" == Just (Float 3.14159)
-- > parse_datum 'd' "3.14159" == Just (Double 3.14159)
-- > parse_datum 's' "\"pi\"" == Just (string "pi")
-- > parse_datum 'b' "[112,105]" == Just (Blob (blob_pack [112,105]))
-- > parse_datum 'm' "(0,144,60,90)" == Just (midi (0,144,60,90))
parse_datum :: Datum_Type -> String -> Maybe Datum
parse_datum :: Datum_Type -> String -> Maybe Datum
parse_datum ty :: Datum_Type
ty =
    case Datum_Type
ty of
      'i' -> (Int32 -> Datum) -> Maybe Int32 -> Maybe Datum
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Int32 -> Datum
Int32 (Maybe Int32 -> Maybe Datum)
-> (String -> Maybe Int32) -> String -> Maybe Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe Int32
forall a. Read a => String -> Maybe a
readMaybe
      'h' -> (Int64 -> Datum) -> Maybe Int64 -> Maybe Datum
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Int64 -> Datum
Int64 (Maybe Int64 -> Maybe Datum)
-> (String -> Maybe Int64) -> String -> Maybe Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe Int64
forall a. Read a => String -> Maybe a
readMaybe
      'f' -> (Float -> Datum) -> Maybe Float -> Maybe Datum
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Float -> Datum
Float (Maybe Float -> Maybe Datum)
-> (String -> Maybe Float) -> String -> Maybe Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe Float
forall a. Read a => String -> Maybe a
readMaybe
      'd' -> (Double -> Datum) -> Maybe Double -> Maybe Datum
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Double -> Datum
Double (Maybe Double -> Maybe Datum)
-> (String -> Maybe Double) -> String -> Maybe Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe Double
forall a. Read a => String -> Maybe a
readMaybe
      's' -> (String -> Datum) -> Maybe String -> Maybe Datum
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (ASCII -> Datum
ASCII_String (ASCII -> Datum) -> (String -> ASCII) -> String -> Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ASCII
Char8.pack) (Maybe String -> Maybe Datum)
-> (String -> Maybe String) -> String -> Maybe Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe String
forall a. Read a => String -> Maybe a
readMaybe
      'b' -> ([Word8] -> Datum) -> Maybe [Word8] -> Maybe Datum
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (BLOB -> Datum
Blob (BLOB -> Datum) -> ([Word8] -> BLOB) -> [Word8] -> Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Word8] -> BLOB
blob_pack) (Maybe [Word8] -> Maybe Datum)
-> (String -> Maybe [Word8]) -> String -> Maybe Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe [Word8]
forall a. Read a => String -> Maybe a
readMaybe
      't' -> String -> String -> Maybe Datum
forall a. HasCallStack => String -> a
error "parse_datum: timestamp not implemented"
      'm' -> ((Word8, Word8, Word8, Word8) -> Datum)
-> Maybe (Word8, Word8, Word8, Word8) -> Maybe Datum
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Word8, Word8, Word8, Word8) -> Datum
midi (Maybe (Word8, Word8, Word8, Word8) -> Maybe Datum)
-> (String -> Maybe (Word8, Word8, Word8, Word8))
-> String
-> Maybe Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe (Word8, Word8, Word8, Word8)
forall a. Read a => String -> Maybe a
readMaybe
      _ -> String -> String -> Maybe Datum
forall a. HasCallStack => String -> a
error "parse_datum: unknown type"

-- | Erroring variant of 'parse_datum'.
parse_datum_err :: Datum_Type -> String -> Datum
parse_datum_err :: Datum_Type -> String -> Datum
parse_datum_err ty :: Datum_Type
ty = Datum -> Maybe Datum -> Datum
forall a. a -> Maybe a -> a
fromMaybe (String -> Datum
forall a. HasCallStack => String -> a
error "parse_datum") (Maybe Datum -> Datum)
-> (String -> Maybe Datum) -> String -> Datum
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Datum_Type -> String -> Maybe Datum
parse_datum Datum_Type
ty