我对Haskell很陌生,而且我正在编写一个程序,我希望在其中创建一个具有创建时间的日志消息,并且可以将当前消息的应用和文本设置为只有消息会改变,但时间将保持创造的时间。 我试着做以下事情:
data Msg = MNothing | MJust UTCTime String
M
用于消息,MNothing
的原因是可能会出现空消息。
我还写道:
instance Monoid Msg where
mempty = MNothing
(MJust t s) `mappend` (MJust t' s') = MJust (minimum (t,t')) (s++s')
通过这种方式,我可以将mappend
用于两条消息,这将是消息中最早的时间。
我的问题是:
使用Msg类型,我在创建MJust
变量时遇到问题,原因是调用getCurrentTime
会返回IO UTCTime
,我希望{ {1}}。我该如何解决这个问题?
我只能UTCTime
两个Msgs,但这样做是没有意义的,是否可以将concat
与正常Msg
联系起来(或{ {1}})?
我是Haskell的新手,所以也许我错过了什么?感谢。
答案 0 :(得分:4)
使用Msg类型,我正在创建一个MJust变量 原因是调用getCurrentTime返回IO UTCTime,我想要 有UTCTime。我该如何解决这个问题?
IO
有许多功能可以帮助解决这些"不匹配问题"
(它们更为通用,也适用于其他类型,但现在我们并不关心。)
重要的一点是,您不能从IO
中提取值。而不是那样,你提升"功能 in IO
!
例如,fmap
函数(也称为liftA
)转换函数,使其在IO
内工作:
fmap :: (a -> b) -> IO a -> IO b
liftA2
让你将两个参数函数应用于IO
内的参数:
liftA2 :: (a -> b -> c) -> IO a -> IO b -> IO c
return :: a -> IO a
在您的情况下,您可以像这样创建IO Msg
类型的值
liftA2 MJust getCurrentTime (pure "somestring")
请记住,您不会从UTCTime
中获得IO
。相反,您需要在IO
内提供所需的一切。
还存在更灵活的函数/运算符(>>=)
(称为" bind"),它允许您构建复合IO
运算,其中第二个运算取决于返回的值第一个:
(>>=) :: IO a -> (a -> IO b) -> IO b
请注意,我们可以根据IO b
的结果确定要执行的IO a
。
一个简单的例子:
getLine >>= \msg -> if msg == "foo" then putStr "yay" else putStr "nay"
再次IO Msg
:
getCurrentTime >>= \theTime -> pure (MJust theTime "foo")
使用(>>=)
,我们可以构造与命令式语言中的语句块非常相似的操作序列。但是编写所有(>>=)
可以得到全面的,所以有一些叫做do-notation的语法糖会让事情变得更容易。
附加说明中的IO Msg
:
do theTime <- getCurrentTime
pure (MJust theTime "foo")
此处<-
不是(>>=)
之类的实际运算符,而是语法糖的一部分。
答案 1 :(得分:1)
至于(2),我解决这个问题的方法是让Msg
成为Functor
:
data Msg a = MNothing | MJust UTCTime a
instance Functor Msg where
fmap f MNothing = MNothing
fmap f (MJust t x) = MJust t (f x)
然后使用字符串连接,您可以使用fmap
:
msg' = fmap (++ " in bed") msg