Reputation: 6793
I've been breaking my head trying to write a multi-arity function on the lines of Development.Shake.cmd
, but even after 8 different attempts across 3 days, I have failed to get this to compile.
Here's what I've managed to get done:
{-# LANGUAGE ExtendedDefaultRules #-}
--
-- this is for an i18n system integrated with Lucid
-- where I want the `t_` function to work with a variable number of arguments
--
module Try4 where
import Lucid (HtmlT, div_, toHtmlRaw)
import Control.Monad.Trans (lift)
import Data.Text as T
default (Text)
newtype Translation = Translation { rawTranslation :: Text }
class (Monad m) => HasI18n m where
fetchTranslation :: Text -> m Translation
class (HasI18n m) => InterpolationValue m a where
-- the reason why this function is not pure, is because it is possible
-- for the conversion function to depend on the monadic context. For example,
-- it is not necessary that date/time values are always converted to text using
-- the same format. The format to be used could be part of the monadic context.
interpolationVal :: a -> m Text
class Interpolate arg result where
t_ :: arg -> result
instance (HasI18n m, a ~ ()) => Interpolate Text (HtmlT m a) where
t_ k = do
x <- lift $ fetchTranslation k
toHtmlRaw $ rawTranslation x
instance (HasI18n m, InterpolationValue m v, a ~ ()) => (Interpolate Text ((Text, v) -> HtmlT m a)) where
t_ k (i, v) = do
x <- lift $ fetchTranslation k
val <- lift $ interpolationVal v
toHtmlRaw $ applySingleInterpolation (rawTranslation x) (i, val)
instance (HasI18n m) => InterpolationValue m Text where
interpolationVal = pure
instance (HasI18n m) => InterpolationValue m Int where
interpolationVal = pure . T.pack . show
applySingleInterpolation :: Text -> (Text, Text) -> Text
applySingleInterpolation memo (k, v) = T.replace ("%{" <> k <> "}") v memo
myHtml :: (HasI18n m) => HtmlT m ()
myHtml = div_ $ do
div_ $ t_ "some.i18n.key"
div_ $ t_ "some.18n.key" ("interpolation1", "some-string")
And finally, here's where I'm stuck. How do I write the (recursive) type-class instance to get the following to compile? No matter what I try, I cannot get the m
of InterpolationValue m a
and HtmlT m a
to unify. Somehow, because of the Interpolate arg result
, GHC seems to get confused and infers a different m1
and m2
.
t_ "some.18n.key"
("interpoliation1", "some-string")
("interpolation2", 10 :: Int)
("interpolation3", fromGregorian 2020 1 1)
Upvotes: 0
Views: 285
Reputation: 9250
I suggest starting from copying Text.Printf and incrementally modifying it til you get what you want. That's the pure source from which the Shake version was copied. The important instances are:
instance (PrintfArg a, PrintfType r) => PrintfType (a -> r) where
instance (a ~ ()) => PrintfType (IO a) where
That's one inductive instance, and one terminal instance.
Upvotes: 0