使用类型号在Haskell中生成给定算术数的函数

使用类型号在Haskell中生成给定算术数的函数,haskell,types,variadic-functions,type-level-computation,Haskell,Types,Variadic Functions,Type Level Computation,假设我将自然数编码为Haskell类型,并且我有一种加减的方法: data Zero data Succ n -- ... 我已经看到了创建可变函数外观的各种代码位,例如,允许以下操作: buildList "polyvariadic" "function" "wut?" :: [String] -- ["polyvariadic","function","wut?"] 我想知道的是,我是否可以在此基础上构建一个函数,该函数只接受与类型号实例对应的参数数。我想做的是: one = Succ

假设我将自然数编码为Haskell类型,并且我有一种加减的方法:

data Zero
data Succ n
-- ...
我已经看到了创建可变函数外观的各种代码位,例如,允许以下操作:

buildList "polyvariadic" "function" "wut?" :: [String]
-- ["polyvariadic","function","wut?"]
我想知道的是,我是否可以在此基础上构建一个函数,该函数只接受与类型号实例对应的参数数。我想做的是:

one = Succ Zero
two = Succ one
three = Succ two

threeStrings :: String -> String -> String -> [String]
threeStrings = buildList three

threeStrings "asdf" "asdf" "asdf"
-- => ["asdf","asdf","asdf"]

threeStrings "asdf"
-- type checker is all HOLY CHRIST TYPE ERROR

threeStrings "asdf" "asdf" "asdf" "asdf"
-- type checker is all SWEET JESUS WHAT YOU ARE DOING

我知道这很愚蠢,可能是在浪费我的时间,但这似乎是周末的乐趣。

好的。对当然,通过围绕递归实例线程化一个数值类型

首先,一些样板:

{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE EmptyDataDecls         #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE ScopedTypeVariables    #-}
您的NAT:

data Zero
data Succ n
变量函数的递归生成器,现在有一个n参数:

class BuildList n a r | r -> a where
    build' :: n -> [a] -> a -> r
基本情况:当我们到达
零时停止:

instance BuildList Zero a [a] where
    build' _ l x = reverse $ x:l
否则,递减1并递归:

instance BuildList n a r => BuildList (Succ n) a (a->r) where
    build' (_ :: Succ n) l x y = build' (undefined :: n) (x:l) y
现在,我们只想循环3次,所以写下:

build :: BuildList (Succ (Succ Zero)) a r => a -> r
build x = build' (undefined :: Succ (Succ Zero)) [] x
完成了

测试:

> build "one" "two" "three" :: [[Char]]
["one","two","three"]
任何较少或较多的错误:

*Main> build "one" "two" "three" "four" :: [[Char]]

<interactive>:1:1:
    No instance for (BuildList Zero [Char] ([Char] -> [[Char]]))

*Main> build "one" "two" :: [[Char]]

<interactive>:1:1:
    No instance for (BuildList (Succ Zero) [Char] [[Char]])
*Main>构建“一”“二”“三”“四”::[[Char]]
:1:1:
没有(BuildList Zero[Char]([Char]->[[Char]])的实例
*Main>build“one”和“two”::[[Char]]
:1:1:
没有(构建列表(成功零)[Char][[Char]]的实例)

我看到您的函数依赖多参数空数据类型灵活地定义了类型变量的范围,并为您提供了Haskell 98版本!它使用hackage上提供的HoleyMonoid:

{-# LANGUAGE NoMonomorphismRestriction #-}

import Prelude hiding (id, (.))
import Control.Category
import Data.HoleyMonoid

suc n = later (:[]) . n

zero  = id
one   = suc zero
two   = suc one
three = suc two

buildList = run
测试(可以省略任何类型签名):


内嵌Martijn的代码提供了一个非常简单的解决方案:

zero xs = xs
suc n xs x = n (xs ++ [x])
buildList n = n []

哦,我的。。。灵活的语境???诺曼同态约束???拜托,伙计们,这不正是TypeFamilies的作用吗

{-# LANGUAGE TypeFamilies #-}
data Zero = Zero
newtype Succ n = Succ n
zero = Zero
one = Succ zero
two = Succ one
three = Succ two
class BuildList n where
    type BL n
    buildListPrefix :: n -> ([String] -> [String]) -> BL n
instance BuildList Zero where
    type BL Zero = [String]
    buildListPrefix Zero h = h []
instance BuildList n => BuildList (Succ n) where
    type BL (Succ n) = String -> BL n
    buildListPrefix (Succ n) h s = buildListPrefix n (h . (s:))
buildList:: BuildList n => n -> BL n
buildList n = buildListPrefix n id

谢谢你,唐!我正朝着那个方向走,但一路上都没弄明白。很高兴看到这一切是如何结合在一起的!
{-# LANGUAGE TypeFamilies #-}
data Zero = Zero
newtype Succ n = Succ n
zero = Zero
one = Succ zero
two = Succ one
three = Succ two
class BuildList n where
    type BL n
    buildListPrefix :: n -> ([String] -> [String]) -> BL n
instance BuildList Zero where
    type BL Zero = [String]
    buildListPrefix Zero h = h []
instance BuildList n => BuildList (Succ n) where
    type BL (Succ n) = String -> BL n
    buildListPrefix (Succ n) h s = buildListPrefix n (h . (s:))
buildList:: BuildList n => n -> BL n
buildList n = buildListPrefix n id