- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我一直在使用免费的 monad 来构建 DSL。作为语言的一部分,有一个 input
命令,目标是在类型级别反射(reflect)输入原语期望的类型,以提高安全性。
例如,我希望能够编写以下程序。
concat :: Action '[String, String] ()
concat = do
(x :: String) <- input
(y :: String) <- input
output $ x ++ " " ++ y
eval :: Action params res -> HList params -> [String]
eval = ...
> eval concat ("a" `HCons` "b" `HCons` HNil)
["a b"]
data HList i where
HNil :: HList '[]
HCons :: h -> HList t -> HList (h ': t)
type family Append (a :: [k]) (b :: [k]) :: [k] where
Append ('[]) l = l
Append (e ': l) l' = e ': (Append l l')
data ActionF next where
Input :: (a -> next) -> ActionF next
Output :: String -> next -> ActionF next
instance Functor ActionF where
fmap f (Input c) = Input (fmap f c)
fmap f (Output s n) = Output s (f n)
data FreeIx f i a where
Return :: a -> FreeIx f '[] a
Free :: f (FreeIx f i a) -> FreeIx f i a
type Action i a = FreeIx ActionF i a
liftF :: Functor f => f a -> FreeIx f i a
liftF = Free . fmap Return
input :: forall a . Action '[a] a
input = liftF (Input id)
output :: String -> Action '[] ()
output s = liftF (Output s ())
bind :: Functor f => FreeIx f t a -> (a -> FreeIx f v b) -> FreeIx f (Append t v) b
bind (Return a) f = f a
bind (Free x) f = Free (fmap (flip bind f) x)
liftF
不进行类型检查。
liftF :: Functor f => Proxy i -> f a -> FreeIx f i a
liftF p = Free . fmap Return
Return
定义的原因和
Free
.
eval :: Action a -> [String] -> a
或类似的东西。这种方法的明显问题是所有参数必须具有相同的类型,并且没有静态保证将提供正确数量的参数。这是解决这个问题的一种尝试。
最佳答案
我找到了一个令人满意的解决方案。以下是最终结果的先睹为快:
addTwo = do
(x :: Int) <- input
(y :: Int) <- input
output $ show (x + y)
eval (1 ::: 2 ::: HNil) addTwo = ["3"]
ActionF
数据类型本身被索引。我们会适应
FreeIx
使用自由的幺半群,列表来构建一个索引的 monad。
Free
FreeIx
的构造函数将需要捕获其两个索引之一的有限性的见证以用于证明。我们将使用
system due to András Kovács for writing proofs about appending type level lists证明结合性和正确的身份。我们将
describe indexed monads in the same manner as Oleg Grenrus .我们将使用
RebindbableSyntax
为
IxMonad
编写表达式的扩展使用普通
do
符号。
RebindbableSyntax
上面提到的我们还需要
UndecidableInstances
用于重用类型系列定义的微不足道的目的。
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE RebindableSyntax #-}
:~:
GADT from Data.Type.Equality
操纵类型相等。
import Data.Type.Equality
import Data.Proxy
Monad
语法,我们将隐藏所有
Monad
来自
Prelude
进口。
RebindableSyntax
扩展用于
do
任何函数的符号
>>=
,
>>
, 和
fail
在范围内。
import Prelude hiding (Monad, (>>=), (>>), fail, return)
HList
中缀构造函数,
:::
.
data HList i where
HNil :: HList '[]
(:::) :: h -> HList t -> HList (h ': t)
infixr 5 :::
Append
类型族
++
镜像
++
列表中的运算符。
type family (++) (a :: [k]) (b :: [k]) :: [k] where
'[] ++ l = l
(e ': l) ++ l' = e ': l ++ l'
forall i. Functor (f i)
的约束很有用.这些在
Haskell
中不存在在捕获约束的 GADT 之外,例如
the Dict
GADT in constraints .就我们的目的而言,定义
Functor
的版本就足够了。带有额外的忽略参数。
class Functor1 (f :: k -> * -> *) where
fmap1 :: (a -> b) -> f i a -> f i b
ActionF
Functor
丢失了一些东西,它无法捕获有关方法要求的类型级别信息。我们将添加一个额外的索引类型
i
捕捉这个。
Input
需要单一类型,
'[a]
, 而
Output
不需要类型,
'[]
.我们将把这个新的类型参数称为仿函数的索引。
data ActionF i next where
Input :: (a -> next) -> ActionF '[a] next
Output :: String -> next -> ActionF '[] next
Functor
和
Functor1
ActionF
的实例.
instance Functor (ActionF i) where
fmap f (Input c) = Input (fmap f c)
fmap f (Output s n) = Output s (f n)
instance Functor1 ActionF where
fmap1 f = fmap f
FreeIx
进行两项更改.我们将改变索引的构建方式。
Free
构造函数将引用来自底层仿函数的索引,并产生
FreeIx
索引是来自基础仿函数的索引和来自包装
++
的索引的自由幺半群和 (
FreeIx
) .我们还将要求
Free
捕获证明基础仿函数的索引是有限的证据。
data FreeIx f (i :: [k]) a where
Return :: a -> FreeIx f '[] a
Free :: (WitnessList i) => f i (FreeIx f j a) -> FreeIx f (i ++ j) a
Functor
和
Functor1
FreeIx
的实例.
instance (Functor1 f) => Functor (FreeIx f i) where
fmap f (Return a) = Return (f a)
fmap f (Free x) = Free (fmap1 (fmap f) x)
instance (Functor1 f) => Functor1 (FreeIx f) where
fmap1 f = fmap f
FreeIx
使用普通的无索引仿函数,我们可以将这些值提升到无约束索引仿函数,
IxIdentityT
.这个答案不需要。
data IxIdentityT f i a = IxIdentityT {runIxIdentityT :: f a}
instance Functor f => Functor (IxIdentityT f i) where
fmap f = IxIdentityT . fmap f . runIxIdentityT
instance Functor f => Functor1 (IxIdentityT f) where
fmap1 f = fmap f
liftF
我们需要证明正确的身份
xs ++ '[] ~ xs
.我们称之为证明
appRightId
用于附加正确的身份。为了写
bind
我们需要证明结合性
xs ++ (yz ++ zs) ~ (xs ++ ys) ++ zs
,我们将称之为
appAssoc
.
type SList xs ~ HFMap Proxy (HList xs)
.
data SList (i :: [k]) where
SNil :: SList '[]
SSucc :: SList t -> SList (h ': t)
SList
对于
xs
的类型列表我们解构和使用
Proxy
对于其他类型的列表,我们可以延迟(可能无限期地)需要
WitnessList
ys
的实例和
zs
.
appAssoc ::
SList xs -> Proxy ys -> Proxy zs ->
(xs ++ (ys ++ zs)) :~: ((xs ++ ys) ++ zs)
appAssoc SNil ys zs = Refl
appAssoc (SSucc xs) ys zs =
case appAssoc xs ys zs of Refl -> Refl
Refl
,
:~:
的构造函数, 只有当编译器拥有两种类型相等的证明时才能构造。模式匹配
Refl
将类型相等的证明引入当前范围。
appRightId :: SList xs -> xs :~: (xs ++ '[])
appRightId SNil = Refl
appRightId (SSucc xs) = case appRightId xs of Refl -> Refl
class WitnessList (xs :: [k]) where
witness :: SList xs
instance WitnessList '[] where
witness = SNil
instance WitnessList xs => WitnessList (x ': xs) where
witness = SSucc witness
appRightId
我们可以将底层仿函数的提升值定义为
FreeIx
.
liftF :: forall f i a . (WitnessList i, Functor1 f) => f i a -> FreeIx f i a
liftF = case appRightId (witness :: SList i) of Refl -> Free . fmap1 Return
forall
适用于
ScopedTypeVariables
.指数有限性的见证,
WitnessList i
,
Free
都需要构造函数和
appRightId
.
appRightId
的证明用于说服编译器
FreeIx f (i ++ '[]) a
构造的类型与
FreeIx f i a
相同.那个
'[]
来自
Return
包裹在底层仿函数中。
input
和
output
, 是按照
liftF
写的.
type Action i a = FreeIx ActionF i a
input :: Action '[a] a
input = liftF (Input id)
output :: String -> Action '[] ()
output s = liftF (Output s ())
RebindableSyntax
我们将定义一个
IxMonad
具有相同函数名称的类
(>>=)
,
(>>)
, 和
fail
如
Monad
但类型不同。此类在
Oleg Grenrus's answer 中有描述.
class Functor1 m => IxMonad (m :: k -> * -> *) where
type Unit :: k
type Plus (i :: k) (j :: k) :: k
return :: a -> m Unit a
(>>=) :: m i a -> (a -> m j b) -> m (Plus i j) b
(>>) :: m i a -> m j b -> m (Plus i j) b
a >> b = a >>= const b
fail :: String -> m i a
fail s = error s
bind
为
FreeIx
需要结合性证明,
appAssoc
.唯一
WitnessList
范围内的实例,
WitnessList i
,是被解构的
Free
捕获的构造函数。再次明确
forall
适用于
ScopedTypeVariables
.
bind :: forall f i j a b. (Functor1 f) => FreeIx f i a -> (a -> FreeIx f j b) -> FreeIx f (i ++ j) b
bind (Return a) f = f a
bind (Free (x :: f i1 (FreeIx f j1 a))) f =
case appAssoc (witness :: SList i1) (Proxy :: Proxy j1) (Proxy :: Proxy j)
of Refl -> Free (fmap1 (`bind` f) x)
bind
是
IxMonad
唯一有趣的部分
FreeIx
的实例.
instance (Functor1 f) => IxMonad (FreeIx f) where
type Unit = '[]
type Plus i j = i ++ j
return = Return
(>>=) = bind
Action xs ()
编写一个简单的解释器以最直接的方式。唯一需要的技巧是避免
HList
上的模式匹配构造函数
:::
直到类型列表之后
i
已知是非空的,因为我们已经在
Input
上匹配了.
eval :: HList i -> Action i () -> [String]
eval inputs action =
case action of
Return () -> []
Free (Input f) ->
case inputs of
(x ::: xs) -> eval xs (f x)
Free (Output s next) -> s : eval inputs next
addTwo
的推断类型感到好奇
addTwo = do
(x :: Int) <- input
(y :: Int) <- input
output $ show (x + y)
> :t addTwo
addTwo :: FreeIx ActionF '[Int, Int] ()
关于haskell - 计算类型索引的自由 monad 的细节,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27676294/
这是我正在调试的函数: boolean adin_memory(char* buffer, int size_chunck, int end_flag){ global_buffer = my
我正在尝试为具有自由 float 底座的机器人计算末端执行器空间速度雅可比行列式。由于自由 float 基数,雅可比应该包含一个基数组件和一个操纵器注释(参见 https://spart.readth
procedure FreeListObjects( l : TStrings); var i : integer; BEGIN FOR i := 0 TO l.Count -1 DO BEG
我正在探索 Haskell 中的选项,这些选项可以让我将业务逻辑与底层系统的技术实现分开。例如,在 Web 服务器的上下文中,将 Web 服务器处理其接收的信息的方式与其读取和写入数据库的方式分开。要
我的目标是使用来自 ActiveMQ 的 WebSphere Liberty Appserver(完整的 Java EE 标准)使用消息。不幸的是,我不知道如何配置 WebSphere Liberty
我以这种方式分配了一个非方阵,但我不确定我是否正确使用了释放 float **matrix_alloc(int m /* rows */, int n /* columns */) { int
我在阅读 refuting the notion 之后的第 13.5 节内置运算符不参与重载决议,并注意到没有关于 operator->* 的部分。它只是一个通用的二元运算符。 它的兄弟operato
我正在尝试使用 Libelf 库来获取有关某些 elf 文件的一些信息。但我不断收到这些“对 [...] 的 undefined reference ”。我从 synaptic 安装了 libelf(
我有创建动态结构数组的波纹管代码。 #include #include #include typedef struct { int flag; char* ip; } ip_mo
我是 StackOverflow 的新人。我现在正在学习C指针。 这是我的代码: #include #include int alloc(int* p){ p = (int*) mallo
我是 StackOverflow 的新人。我现在正在学习C指针。 这是我的代码: #include #include int alloc(int* p){ p = (int*) mallo
我正在用 C 编写一个程序,我需要读入一个文件并打印出每个至少 4 个字符长的字符串。我在分配要使用的内存时遇到问题。字符串可以任意长。我试图将缓冲区分配给文件的大小,然后在最后释放它,但我显然错过了
我尝试用 C 语言编写 ls 命令,但 -R 选项有问题。 输出: /Applications/Atom.app/Contents/Resources/app/apm/node_modules/es5
我正在编写一个 shell,但在执行内存检查时遇到问题,因为 valgrind 无法正常运行。 我遇到了这样的错误(我自己杀死了它): ==19703== Memcheck, a memory err
我有这样一段代码: void *write_parallel(void *num_for_chunk) { struct rusage *sum = (struct rusage*) mall
当使用包含 200-300 个整数(以空格分隔)的输入 .txt 文件运行此代码时,我在使用 fprintf 语句的 for 循环之前收到错误。 我不确定 qsort 是否导致了此错误或为什么会发生此
我试图告诉 Websphere Liberty 我的 log4j2.xml 文件在哪里,但它不起作用。 在我的文件 jvm.options 中,我配置: -Dlog4j.configurationFi
从 websphere liberty 16 迁移到 19.0.0.1 我遇到以下异常:运行存储过程后关闭连接,出现以下异常: EJB threw an unexpected (non-declare
当对大小为 210*8 的种子数据集运行此代码时,我在预测函数中的 qsort() 行之后收到错误。它不在 qsort() 之后执行。 我不确定 qsort 是否导致了此错误或为什么会发生此错误,但如
这个问题已经有答案了: Facing an error "*** glibc detected *** free(): invalid next size (fast)" (2 个回答) 已关闭 9
我是一名优秀的程序员,十分优秀!