Рассмотрим этот код:
import Data.Maybe (fromMaybe)
data MyStructure = Foo Int | Bar String MyStructure | Baz MyStructure MyStructure | Qux Bool Bool MyStructure MyStructure deriving(Eq,Show)
makeReplacements :: [(MyStructure, MyStructure)] -> MyStructure -> MyStructure
makeReplacements replacements structure = fromMaybe (descend structure) (lookup structure replacements)
where
descend :: MyStructure -> MyStructure
descend (Foo x) = Foo x
descend (Bar x y) = Bar x (makeReplacements replacements y)
descend (Baz x y) = Baz (makeReplacements replacements x) (makeReplacements replacements y)
descend (Qux x y z w) = Qux x y (makeReplacements replacements z) (makeReplacements replacements w)
Он определяет рекурсивный тип данных и функцию, которая выполняет поиск и замену, просматривая его. Однако я использую явную рекурсию и вместо этого хотел бы использовать схему рекурсии.
Сначала я добавил makeBaseFunctor ''MyStructure
. Для ясности я расширил получившийся Template Haskell и производный экземпляр Functor ниже. Затем я смог переписать descend
:
{-# LANGUAGE DeriveTraversable, TypeFamilies #-}
import Data.Maybe (fromMaybe)
import Data.Functor.Foldable (Base, Recursive(..), Corecursive(..))
data MyStructure = Foo Int | Bar String MyStructure | Baz MyStructure MyStructure | Qux Bool Bool MyStructure MyStructure deriving(Eq,Show)
makeReplacements :: [(MyStructure, MyStructure)] -> MyStructure -> MyStructure
makeReplacements replacements structure = fromMaybe (descend structure) (lookup structure replacements)
where
descend :: MyStructure -> MyStructure
descend = embed . fmap (makeReplacements replacements) . project
-- begin code that would normally be auto-generated
data MyStructureF r = FooF Int | BarF String r | BazF r r | QuxF Bool Bool r r deriving(Foldable,Traversable)
instance Functor MyStructureF where
fmap _ (FooF x) = FooF x
fmap f (BarF x y) = BarF x (f y)
fmap f (BazF x y) = BazF (f x) (f y)
fmap f (QuxF x y z w) = QuxF x y (f z) (f w)
type instance Base MyStructure = MyStructureF
instance Recursive MyStructure where
project (Foo x) = FooF x
project (Bar x y) = BarF x y
project (Baz x y) = BazF x y
project (Qux x y z w) = QuxF x y z w
instance Corecursive MyStructure where
embed (FooF x) = Foo x
embed (BarF x y) = Bar x y
embed (BazF x y) = Baz x y
embed (QuxF x y z w) = Qux x y z w
-- end code that would normally be auto-generated
Если бы я остановился на этом, я бы уже выиграл: мне больше не нужно выписывать все падежи в descend
, и я не могу случайно сделать ошибку типа descend (Baz x y) = Baz x (makeReplacements replacements y)
(забыв заменить внутри x
). Однако здесь все еще присутствует явная рекурсия, так как я все еще использую makeReplacements
из его собственного определения. Как я могу переписать это, чтобы удалить это, чтобы я выполнял всю свою рекурсию внутри схем рекурсии?
descend
мне кажется параморфизмом. Вы хотите сначала посмотреть на узел, который нужно свернуть, чтобы увидеть, следует ли его заменить, а если нет, то вы посмотрите на уже рекурсивно свернутый результат, который даст вам катаморфизм. Имеет ли подпись para, специализированный для ваших типов, выглядит многообещающе? - person amalloy   schedule 01.10.2019para
это(Base t (t, a) -> a) -> t -> a
. Для меня это выглядит близко, но не совсем идеально. Разве я не хотел бы на самом деле((t, Base t a) -> a) -> t -> a
или((t, Base t (t, a)) -> a) -> t -> a
, чтобы я мог смотреть на элемент, на котором я нахожусь? - person Joseph Sible-Reinstate Monica   schedule 01.10.2019