2015-12-15 85 views
8

可以將免費單片機翻譯爲任何其他單片機,但給定類型爲Free f x的值時,我想打印整棵樹,而不是將生成的AST的每個節點映射到另一單片機中的某個其他節點。打印免費單片機

加布裏埃爾岡薩雷斯uses值直接

showProgram :: (Show a, Show r) => Free (Toy a) r -> String 
showProgram (Free (Output a x)) = 
    "output " ++ show a ++ "\n" ++ showProgram x 
showProgram (Free (Bell x)) = 
    "bell\n" ++ showProgram x 
showProgram (Free Done) = 
    "done\n" 
showProgram (Pure r) = 
    "return " ++ show r ++ "\n" 

可以抽象出來作爲

showF :: (x -> b) -> ((Free f x -> b) -> f (Free f x) -> b) -> Free f x -> b 
showF backLiftValue backLiftF = fix (showFU backLiftValue backLiftF) 
    where 
     showFU :: (x -> b) -> ((Free f x -> b) -> f (Free f x) -> b) -> (Free f x -> b) -> Free f x -> b 
     showFU backLiftValue backLiftF next = go . runIdentity . runFreeT where 
      go (FreeF c) = backLiftF next c 
      go (Pure x) = backLiftValue x 

這是容易的,如果我們有像(使用Choice x = Choice x x作爲仿)

一個多態函數調用
showChoice :: forall x. (x -> String) -> Choice x -> String 
showChoice show (Choice a b) = "Choice (" ++ show a ++ "," ++ show b ++ ")" 

但是,這似乎相當複雜用於簡單操作... 還有什麼其他方法可以從f x -> bFree f x -> b

回答

9

使用iterfmap

{-# LANGUAGE DeriveFunctor #-} 

import Control.Monad.Free 

data Choice x = Choice x x deriving (Functor) 

-- iter :: Functor f => (f a -> a) -> Free f a -> a 
-- iter _ (Pure a) = a 
-- iter phi (Free m) = phi (iter phi <$> m) 

showFreeChoice :: Show a => Free Choice a -> String 
showFreeChoice = 
     iter (\(Choice l r) -> "(Choice " ++ l ++ " " ++ r ++ ")") 
    . fmap (\a -> "(Pure " ++ show a ++ ")") 

fmap轉換從Free f aFree f b,並iter沒有休息。你可以考慮這一點,也許會得到更好的表現:

iter' :: Functor f => (f b -> b) -> (a -> b) -> Free f a -> b 
iter' f g = go where 
    go (Pure a) = g a 
    go (Free fa) = f (go <$> fa) 
+0

啊,這很好!謝謝。現在我發現很明顯,人們必須尋找將'f'的代數翻譯爲'Free f'的代數。 – nicolas

+0

我喜歡你的'iter''。我試圖最近找到一些服務於這個普通用途的東西(感覺自己肯定有一個),但某種方式未能擊中正確的類型。 – dfeuer

+1

這可能是有價值的基準對'iter'f g =去哪裏......。有些測量表明,當至少有兩個參數在遞歸中保持不變時,這往往是好的。 – dfeuer