在编译时生成类型级别的Nats以进行约束检查

时间:2019-01-05 22:50:43

标签: haskell types

我正在尝试在编译时以类型级别表示一个整数值,以便可以根据一些约束对其进行检查。

我希望DSL用户不必写类似的东西:

five = inc $ inc $ inc $ inc $ inc $ zero

因此,我一直试图创建的是一个将那个值提升到类型级别的函数。

fromInt :: Int -> repr n
fromInt n = __

这是我到目前为止尝试过的:

data Z
data S a

data IntNat a = IN Int

zero = (IN 0 :: IntNat Z)

inc :: IntNat a -> IntNat (S a)
inc (IN i) = (IN (i + 1))

fromInt :: Int -> IntNat a 
fromInt 0 = (IN 0 :: IntNat Z)
fromInt n = inc (fromInt (n - 1))

但是,这失败了,因为我对IntNat IntNat Z ~/~ IntNat S Z没有足够的概括。

这种方法是否普遍存在缺陷,还是需要将S Z封装在类型族/类型类中?

这的另一个经典示例是使用类型级别长度注释的向量时生成特定长度的向量。该功能应该解决我遇到的相同问题。

2 个答案:

答案 0 :(得分:3)

由于Haskell没有依赖类型,因此无法将值级别image.setImageObserver(button)提升为自然类型。根据您的用例,您有几种近似选择。

使用延续传递样式

arr = ["bunny", "watch", "book"]

title = ("The book of coding. (e-book) by Seb Tota").lower()
length = len(arr)
for i in range(0,length - 1):
    if arr[i] in title:
        print "dont click"
    else:
        print "click"

使用存在类型

                .addOnChildClickListener(new ExpandableListView.OnChildClickListener() {
                    @Override
                    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                        navigationExpandableListView.setSelected(groupPosition, childPosition);

                        if (groupPosition == 0)

                        if (groupPosition == 1)

                        if (groupPosition == 2)

                            if (id == 0) {
                                Common.showToast(context, "Man's Fashion");
                                Intent intent = new Intent(MainActivity.this, test.class);
                                startActivity(intent);
                            } else if (id == 1) {
                                Common.showToast(context, "Woman's Fashion");
                            } else if (id == 2) {
                                Common.showToast(context, "Babies and Family");
                            } else if (id == 3) {
                                Common.showToast(context, "Health");
                            }

                        drawer.closeDrawer(GravityCompat.START);
                        return false;
                    }
                });

        navigationExpandableListView.expandGroup(2);
    }

利用现有的类型级别文字

Int

答案 1 :(得分:2)

  

我希望DSL用户不必写类似的东西:

five = inc $ inc $ inc $ inc $ inc $ zero

您可以轻松地为它写一个准引号,但是在这种情况下,您可以使用GHC对类型级Nat文字的支持,使用类型族(它们是类型级函数)转换为表示形式。 / p>

{-# LANGUAGE DataKinds, TypeOperators, KindSignatures, TypeFamilies, UndecidableInstances  #-}
import GHC.TypeLits
import Data.Proxy

data Z
data S a

type family FromNat (n :: Nat) where
  FromNat 0 = Z
  FromNat n = S (FromNat (n - 1))

fromNat :: Proxy n -> Proxy (FromNat n)
fromNat _ = Proxy

您还会得到诸如类型级加法之类的东西

*Main GHC.TypeLits Data.Proxy> :t fromNat (Proxy :: Proxy 5)
fromNat (Proxy :: Proxy 5) :: Proxy (S (S (S (S (S Z)))))
*Main GHC.TypeLits Data.Proxy> :t fromNat (Proxy :: Proxy (3 + 2))
fromNat (Proxy :: Proxy (3 + 2)) :: Proxy (S (S (S (S (S Z)))))

编辑:首先给出答案,但将其作为替代实现