2015-05-08 103 views
4

我想知道是否有Applicative可以跟蹤已發生多少應用操作。我試圖執行它如下:適用於增加haskell中的環境

import Control.Applicative 

main :: IO() 
main = print $ run 1 $ (,,,) <$> FromInt id <*> FromInt id <*> FromInt id <*> FromInt id 

data FromInt a = FromInt (Int -> a) 

run :: Int -> FromInt a -> a 
run i (FromInt f) = f i 

instance Functor FromInt where 
    fmap g (FromInt f) = FromInt (g . f) 

instance Applicative FromInt where 
    pure a = FromInt (const a) 
    FromInt f <*> FromInt g = FromInt (\i -> f i (g (i + 1))) 

但是,這當然不起作用。如果我們在文件中調用runhaskell,我們得到這樣的:

(1,2,2,2) 

而我想是這樣的:

(1,2,3,4) 

我見過的人推的要求完成這個效果遞增到實際數據(這是yesod-forms如何實現formlet風格的實現)。這或多或少使用State上的變體,並且如果人們不使用特定的輔助函數(我認爲這個函數被稱爲mhelper),它允許人們打破假定的不變量。我想知道增量是否可以像我試圖做的那樣進入應用實例。這會違反這個不變的不可能。

+0

我不相信這是可能的,因爲應用性的行動不能依賴於先前的結果。要做到這一點,我認爲你必須有一個monad來獲得你想要的輸出。 – bheklilr

+0

你不需要單子。 「State」的應用實例工作正常,但我希望有更好的方法。 –

回答

10

(,) aApplicativeaMonoid。我們可以使用Data.Functor.Compose與其他應用程序合成(,) (Sum Int),並獲得一個應用程序,讓我們在運行之前估算分配給計算的「成本」。

計數步驟,我們需要從基礎應用性總是分配成本1升降功能:

module Main where 

import Data.Monoid 
import Control.Applicative 
import Data.Functor.Compose 

type CountedIO a = Compose ((,) (Sum Int)) IO a 

-- lift from IO 
step :: IO a -> CountedIO a 
step cmd = Compose (Sum 1, cmd) 

countSteps :: CountedIO a -> Int 
countSteps = getSum . fst . getCompose 

exec :: CountedIO a -> IO a 
exec = snd . getCompose 

program :: CountedIO() 
program = step (putStrLn "aaa") *> step (putStrLn "bbb") *> step (putStrLn "ccc") 

main :: IO() 
main = do 
    putStrLn $ "Number of steps: " ++ show (countSteps program) 
    exec program 

爲安全起見,我們可以隱藏NEWTYPE背後的組成aplicative,而不是出口構造函數,只有step函數。

(與pure創建行動花費了0,不算作一個步驟。)

+0

這是一個很好的解決方案,但最終我試圖避免這樣做。如果沒有人能夠提出更好的方法(不需要隱藏數據構造函數來保持不變),我會等一會兒並接受它。 –

+7

@Andrew Thaddeus Martin我認爲在保留適用法律的同時,將難以拉入應用程序是很困難的。如果'(<*>)'總是增加計數,那麼該怎麼制定法律'純ID <*> v = v'? – danidiaz

+0

你是對的。我正在尋找的實例證明是不可能的。謝謝。 –

相關問題