1

以下は、私の前の質問に対する Petr Pudlak の解決策に基づいた、MonadState のレンズ (Edward Kmett による) に関する一連の例/演習です。

これらの例は、いくつかの用途とレンズの能力を示すだけでなく、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 によって生成されたものです。もう 1 つは、Peter のものからの翻案です。個人的には、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
4

2 に答える 2

5

ML の伝統のファミリーとして、Haskell は、すべてのトップレベル バインディングが最も一般的な型を持つように特別に設計されており、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、異なる profunctor p、モナド変換子、tおよび statesを選択できるため、これは非常に一般的な型sです。とてもmodif2再利用可能です。あなたはこの型シグネチャを好みます:

modif2 :: (Int -> Int -> Int) -> Lens' a Int -> StateT a IO ()     

pこれは読みやすいですが、一般的ではないこと->に同意します。tStateTmodif2

今後改善される見込みはありますか?

Haskell は、型推論の結果として、最も一般的な型を引き続き義務付けることになると確信しています。最も一般的な type に加えて、ghci またはサードパーティのツールがインスタンス化の例を示すことができると想像できます。この場合、それが典型的なprofunctorで->あると何らかの方法で宣言するとよいでしょう。しかし、私はこの方向での作業を認識していないので、あまり希望はありません。

于 2014-05-02T19:50:22.963 に答える
4

最初の例を見てみましょう:

modif :: Lens' a String -> StateT a IO ()
modif l = do
  s <- lift getLine
  l %= (++ s)

このタイプは単純ですが、欠点もありますLensこれは完全に理にかなっていますが、Isoare a がある場合は関数を使用できません! GHCi が推論するより一般的な型を考えると、例えば次のように書くことができます:Traversal

modif _Just :: StateT (Maybe String) IO ()

その状態がJust、または

modif traverse :: StateT [String] IO ()

リスト内のすべての要素に読み取り値を追加します。_Justtraverseはレンズではなく、 のみであるため、これはあなたが与えた単純なタイプでは不可能Traversalsです。

于 2014-05-02T20:05:52.977 に答える