本杰明·霍奇森的解决方案更简单,更优雅,但这里有一个类型级编程解决方案,使用singletons
包的机器。
这个想法是策略表示为类型级别列表(Role, Operation)
元组,其中Role
和Operation
必须在整个列表中不下降。这样,我们不能有一个荒唐的[(Public,Edit),(Owner,View)]
权限。
一些必要的扩展和进口:
{-# language PolyKinds #-}
{-# language DataKinds #-}
{-# language TypeFamilies #-}
{-# language GADTs #-}
{-# language TypeOperators #-}
{-# language UndecidableInstances #-}
{-# language FlexibleInstances #-}
{-# language ScopedTypeVariables #-}
{-# language TemplateHaskell #-}
import Data.Singletons
import Data.Singletons.TH
import Data.Promotion.Prelude (Unzip)
我们宣布的数据类型和使用singletonize这些模板哈斯克尔:
data Role = Public | Contributor | Owner deriving (Show,Eq,Ord)
data Operation = None | View | Edit deriving (Show,Eq,Ord)
$(genSingletons [''Role,''Operation])
$(promoteEqInstances [''Role,''Operation])
$(promoteOrdInstances [''Role,''Operation])
一种列表类与非减元素:
class Monotone (xs :: [k])
instance Monotone '[]
instance Monotone (x ': '[])
instance ((x :<= y) ~ True, Monotone (y ': xs)) => Monotone (x ': y ': xs)
给定一个指定为类型级别列表的策略,返回策略函数:
policy :: forall (xs :: [(Role, Operation)]) rs os.
(Unzip xs ~ '(rs,os), Monotone rs, Monotone os)
=> Sing xs
-> Role
-> Operation
policy singleton role =
let decreasing = reverse (fromSing singleton)
allowed = dropWhile (\(role',_) -> role' > role) decreasing
in case allowed of
[] -> None
(_,perm) : _ -> perm
测试在ghci中试验:
ghci> :set -XDataKinds -XPolyKinds -XTypeApplications
ghci> policy (sing::Sing '[ '(Public,View),'(Owner,Edit) ]) Owner
Edit
ghci> policy (sing::Sing '[ '(Public,Edit),'(Owner,View) ]) Owner
*unhelpful type error*
我是正确的,GHC能够推导一个Monoid实例'Policy'因为'最大了'是一个Monoid和'X - >含半幺群y'是一个Monoid。我也可以派生自己的'实例:''(Policy a)'mappend'(Policy b)= Policy $ \ r - > max(ar)(br)'' – homam
是的,虽然GHC会产生完全相同的代码,那么为什么要写它呢? –
这是一个非常优雅的解决方案。谢谢! – homam