下面是一系列关于镜头的例子/练习(由Edward )在MonadState中,基于Pudlak对我之前的问题的解答。
除了演示镜头的一些用途和威力外,这些示例还显示了理解GHCi生成的类型签名是多么困难。还有希望在未来的事情会有所改善吗?
{-# LANGUAGE TemplateHaskell, RankNTypes #-}
import Control.Lens
import Control.Monad.State
---------- Example by Petr Pudlak ----------
-- | An example of a universal function that modifies any lens.
-- It reads a string and appends it to the existing value.
modif :: Lens' a String -> StateT a IO ()
modif l = do
s <- lift getLine
l %= (++ s)
-----------------------------------------------下面的注释类型签名是由GHCi生成的签名。另一种是彼得的改编。就我个人而言,我比GHCi所产生的更难理解,我想知道:为什么GHCi不产生那些简化的?
-------------------------------------------
-- modif2
-- :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
-- (Int -> p a b) -> Setting p s s a b -> t IO ()
modif2 :: (Int -> Int -> Int) -> Lens' a Int -> StateT a IO ()
modif2 f l = do
s<- lift getLine
l %= f (read s :: Int)
---------------------------------------
-- modif3
-- :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
-- (String -> p a b) -> Setting p s s a b -> t IO ()
modif3 :: (String -> Int -> Int) -> Lens' a Int -> StateT a IO ()
modif3 f l = do
s <- lift getLine
l %= f s
-- :t modif3 (\n -> (+) (read n :: Int)) == Lens' a Int -> StateT a IO ()
---------------------------------------
-- modif4
-- :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
-- (t1 -> p a b) -> (String -> t1) -> Setting p s s a b -> t IO ()
modif4 :: (Bool -> Bool -> Bool) -> (String -> Bool) -> Lens' a Bool -> StateT a IO ()
modif4 f f2 l = do
s <- lift getLine
l %= f (f2 s)
-- :t modif4 (&&) (\s -> read s :: Bool) == Lens' a Bool -> StateT a IO ()
---------------------------------------
-- modif5
-- :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
-- (t1 -> p a b) -> (String -> t1) -> Setting p s s a b -> t IO ()
modif5 :: (b -> b -> b) -> (String -> b) -> Lens' a b -> StateT a IO ()
modif5 f f2 l = do
s<- lift getLine
l %= f (f2 s)
-- :t modif5 (&&) (\s -> read s :: Bool) == Lens' a Bool -> StateT a IO ()
---------------------------------------
-- modif6
-- :: (Profunctor p, MonadState s m) =>
-- (t -> p a b) -> (t1 -> t) -> t1 -> Setting p s s a b -> m ()
modif6 :: (b -> b -> b) -> (c -> b) -> c -> Lens' a b -> StateT a IO ()
modif6 f f2 x l = do
l %= f (f2 x)
-- :t modif6 (&&) (\s -> read s :: Bool) "True" == MonadState s m => Setting (->) s s Bool Bool -> m ()
-- :t modif6 (&&) (\s -> read s :: Bool) "True"
---------------------------------------
-- modif7
-- :: (Profunctor p, MonadState s IO) =>
-- (t -> p a b) -> (String -> t) -> Setting p s s a b -> IO ()
modif7 :: (b -> b -> b) -> (String -> b) -> Lens' a b -> StateT a IO ()
modif7 f f2 l = do
s <- lift getLine
l %= f (f2 s)
-- :t modif7 (&&) (\s -> read s :: Bool) ==
-- :t modif7 (+) (\s -> read s :: Int) ==
---------------------------------------
p7a :: StateT Int IO ()
p7a = do
get
modif7 (+) (\s -> read s :: Int) id
test7a = execStateT p7a 10 -- if input 30 then result 40
---------------------------------------
p7b :: StateT Bool IO ()
p7b = do
get
modif7 (||) (\s -> read s :: Bool) id
test7b = execStateT p7b False -- if input "True" then result "True"
---------------------------------------
data Test = Test { _first :: Int
, _second :: Bool
}
deriving Show
$(makeLenses ''Test)
dataTest :: Test
dataTest = Test { _first = 1, _second = False }
monadTest :: StateT Test IO String
monadTest = do
get
lift . putStrLn $ "1) modify \"first\" (Int requested)"
lift . putStrLn $ "2) modify \"second\" (Bool requested)"
answ <- lift getLine
case answ of
"1" -> do lift . putStr $ "> Write an Int: "
modif7 (+) (\s -> read s :: Int) first
"2" -> do lift . putStr $ "> Write a Bool: "
modif7 (||) (\s -> read s :: Bool) second
_ -> error "Wrong choice!"
return answ
testMonadTest :: IO Test
testMonadTest = execStateT monadTest dataTest发布于 2014-05-02 19:50:22
作为ML传统中的一个家族,Haskell是专门设计的,这样每个toplevel绑定都有一个最通用的类型,并且Haskell实现可以而且必须推断出这种最通用的类型。这确保了您可以在尽可能多的地方重用绑定。在某种程度上,这意味着类型推断永远不会出错,因为无论您想到哪种类型,类型推断都会找出相同的类型或更一般的类型。
为什么GHCi不产生那些简化的?
它反而找出了更一般的类型。例如,您提到GHC为某些代码确定了以下类型:
modif2 :: (Profunctor p, MonadTrans t, MonadState s (t IO)) =>
(Int -> p a b) -> Setting p s s a b -> t IO ()这是一种非常普遍的类型,因为每次我使用modif2时,我都可以选择不同的p、单端变压器t和状态s。所以modif2是非常可重用的。您更喜欢这种类型的签名:
modif2 :: (Int -> Int -> Int) -> Lens' a Int -> StateT a IO () 我同意这更易读,但也不那么通用:这里您决定p必须是->,t必须是StateT,而作为modif2的用户,我无法改变这一点。
还有希望在未来的事情会有所改善吗?
我确信,作为类型推断的结果,Haskell将继续强制大多数一般类型。我可以想象,除了最通用的类型之外,ghci或第三方工具还可以向您展示示例实例化。在这种情况下,最好以某种方式声明->是一个典型的终止函数。我不知道在这个方向上有什么工作,所以没有太多的希望,没有。
发布于 2014-05-02 20:05:52
让我们看一下第一个例子:
modif :: Lens' a String -> StateT a IO ()
modif l = do
s <- lift getLine
l %= (++ s)这种类型很简单,但也有一个缺点:您只能使用传递Lens的函数。您不能使用您的函数,当您有一个Iso是一个Traversal,即使这将是完全合理的!考虑到GHCi推断的更一般的类型,例如,您可以编写以下内容:
modif _Just :: StateT (Maybe String) IO ()只有在状态为Just时才会追加读值,或者
modif traverse :: StateT [String] IO ()它将将读值附加到列表中的所有元素。对于你给出的简单类型来说,这是不可能的,因为_Just和traverse不是透镜,而是Traversals。
https://stackoverflow.com/questions/23435676
复制相似问题