收集某种类型的构造函数的参数
对this后续问题,假设我有两个t1和t2的某个代数数据类型的术语,并且检查到t1和t2的构造函数是相同的。也就是说,(非正式),T1 = F(S)和t2 = G(T),我已经检查了F = G。现在,我想计算收集某种类型的构造函数的参数
map f (zip S T)
假设S和T是名单参数。这个天真的代码会要求S中的所有东西都是单一类型的,但一般情况并非如此。
在这一点上,我只是好奇,如果有办法做到这一点。看起来像构造函数的套件将是一个更简单的解决方案。我想写一个泛型类型,但我只需要它的某种特定类型。
编辑:我对这个问题的描述不太对。我使用的类型是一样的东西
data Term v = F (Term v) (Term v) | G (Term v)
| C
| Var v
对于Term v
类型的零个或多个参数(如(F x y, F z w)
),我想申请到他们每个人的功能,并收集结果的列表构造函数:[f (x,z), f (y,w)]
,我想忽略这些变量。
我假设Term v
类型是Unifiable v
,它有一个方法isVar
,它挑选出我的类型中的哪些项是变量。但是鉴于类型可以具有任意参数的构造函数,所以我不确定我首先可以得到什么概括性。我需要类似于那里有一个特定的Var
构造函数,以及所有其他构造函数的形式为F [Term v]
,或者这样的,我不知道我需要保证什么约束。
编辑:更具体地说,我试图定义一个函数(在假哈斯克尔)
match :: (Variable v) => Term v -> Term v -> Maybe [(v, Term v)] match t1 t2 = case t1 of
Var v -> Just (check v t2)
f xs -> case t2 of
Var v -> Just (check t1 v)
g ys -> if f == g then flatten(map match (zip (xs,ys)))
else Nothing
当然,你不能使用的情况下那样,这种假设每个构造(除了Var)以一个列表作为它的参数。
回答:
下面是通用编程的one-liner库的外观。有一些样板可以封装在某个地方,也许只有一行。
的zipWithA
说对match'
递归调用已键入forall s. Typeable s => s -> s -> ZeroA Unifier s
,其中ZeroA
一定适用函子的参数。理想情况下,我们希望s
等于Term
,但one-liner
需要一个可处理泛型类型的所有字段的函数(您可以选择一个必须适用于所有字段的约束);我们使用Typeable
(通过withType
)过滤出无效的情况。
main.hs
:
{-# LANGUAGE DeriveGeneric, TypeApplications #-} import Data.Typeable (Typeable)
import Generics.OneLiner (zipWithA)
import GHC.Generics (Generic)
import MyLibrary -- Some setup that should probably go in a library
-- Some arbitrary syntax
type V = Int
data Term = Var V | C | UnOp Term | BinOp Term Term
deriving (Show, Generic)
type Unifier = [(Int, Term)]
match :: Term -> Term -> Maybe Unifier
match t1 t2 = unZeroA (match' t1 t2)
-- ZeroA is a wrapper around the applicative functor Const
match' :: Term -> Term -> ZeroA Unifier Term
match' (Var v1) t2 = write (v1, t2)
match' t1 (Var v2) = write (v2, t1)
match' t1 t2 = zipWithA @Typeable f t1 t2
where
f :: Typeable s => s -> s -> ZeroA Unifier s
f s1 s2 = withType @Term s1 (match' s1 s2)
main = do
print (match (BinOp (Var 0) (UnOp (UnOp (Var 1))))
(BinOp C (UnOp (Var 4))))
-- Just [(0,C),(4,UnOp (Var 1))]
print (match (BinOp C C) (UnOp C))
-- Nothing
MyLibrary.hs
:
{-# LANGUAGE AllowAmbiguousTypes, DeriveGeneric, FlexibleInstances, GADTs, RankNTypes, ScopedTypeVariables, TypeOperators #-} module MyLibrary where
import Control.Applicative (Alternative(..), Const(..))
import Data.Typeable (Typeable, eqT, (:~:)(..))
-- Add an absorbing element to any Monoid b
newtype Zero b = Zero { unZero :: Maybe b }
nil :: Zero b
nil = Zero Nothing
toZero :: b -> Zero b
toZero b = Zero (Just b)
instance Monoid b => Monoid (Zero b) where
mempty = Zero (Just mempty)
Zero Nothing `mappend` _ = nil
_ `mappend` Zero Nothing = nil
Zero a `mappend` Zero b = Zero (a `mappend` b) -- reusing the Maybe Monoid.
-- Every monoid induces an Applicative functor via Const.
type ZeroA b = Const (Zero b)
unZeroA :: ZeroA b a -> Maybe b
unZeroA = unZero . getConst
-- A writer-like action.
write :: b -> ZeroA [b] a
write b = Const (toZero [b])
-- A monoid with an absorbing element induces an Alternative functor
instance Monoid b => Alternative (ZeroA b) where
empty = Const nil
Const (Zero Nothing) <|> y = y
x <|> _ = x
-- Typeable helper
-- withType @t x (body x):
-- the body may assume that the type of x is equal to t.
--
-- If that is actually the case, then
-- withType @t x (body x) = body x
-- otherwise
-- withType @t x (body x) = empty
withType
:: forall t s f a
. (Typeable s, Typeable t, Alternative f)
=> s -> ((t ~ s) => f a) -> f a
withType _ body = case eqT :: Maybe (s :~: t) of
Nothing -> empty
Just Refl -> body
以上是 收集某种类型的构造函数的参数 的全部内容, 来源链接: utcz.com/qa/260962.html