小编Blu*_*ula的帖子

如何删除 Trees That Grow 引入的所有样板文件?

我正在尝试用 Haskell 定义一种编程语言。我希望使 AST 可扩展:AST 模块的用户(例如漂亮的打印机、解释器、编译器、类型系统、语言服务器等)应该能够通过添加新功能和新数据(用于扩展语法的新数据类型以及当前数据构造函数的新字段,以存储各个组件所需的数据)。

我尝试通过使用Trees That Grow (TTG)来实现这个目标。它有效,但会导致太多的样板文件。我的最小原型的代码行数增加了 10 倍,并且这个数字随着 AST 大小乘以扩展数量而增长。更改一些较小的内容需要更改 AST 模块的几行,而更改扩展性实现方式的某些内容则需要重写大部分内容。

有没有什么方法可以减少所需的样板数量,或者最好将其完全删除?

到目前为止我所拥有的代码示例

“基础”AST

这只是 AST 的一小部分。它与 JSON 非常相似,因为我决定从一个小型原型开始。

module AST ( KeyValue(..), Data(..) ) where

data KeyValue = KV String Data deriving (Show, Eq, Ord)

data Data =
    Null |
    Int Int |
    Num Double |
    Bool Bool |
    String String |
    Array [Data] |
    Object [KeyValue] deriving (Show, Eq, Ord)
Run Code Online (Sandbox Code Playgroud)

可扩展的 AST,来自 Trees That Grow

为了通过 TTG 扩展它,数据类型变成这样:

data KeyValueX x =
    KVX …
Run Code Online (Sandbox Code Playgroud)

haskell boilerplate

10
推荐指数
1
解决办法
315
查看次数

for...of 迭代器而不关闭它

for...of当我中断循环时,是否可以循环迭代器的一部分而不关闭迭代器?

例子:

function* numbers(i=0){
  while(true) yield i++;
}

let nums=numbers();

// this loop prints numbers from 0 to 3
for(const n of nums){
  if(n>3) break;
  console.log(n);
}

// this loop doesn't print anything because `nums` has already been closed
for(const n of nums){
  if(n>10) break;
  console.log(n);
}
Run Code Online (Sandbox Code Playgroud)

我知道我可以通过自己的调用来遍历迭代器iterator.next()。但我想知道是否可以用for...of语法来做到这一点。

javascript iterator for-of-loop

7
推荐指数
1
解决办法
115
查看次数

我如何快速了解 Haskell 如何解析表达式?是否可以在每个非叶 AST 节点周围打印带括号的表达式?

有时我会遇到充满不同运算符的表达式,但我不知道它们是如何解析的。我知道我可以使用 询问有关运算符的 GHCi 信息:i,但必须查看多个运算符并应用它们的优先级和关联性来在我的脑海中重建它感觉相当困难。

\n

有没有更简单的理解方法?

\n
例子
\n

例如,采用以下有效的 Haskell 表达式:

\n
7 \xc2\xa4 2 \xe2\x8f\x86 4 \xe2\x8f\x86 3 \xc2\xa4 1 \xe2\x99\x82 3 \xe2\x99\x82 2\n
Run Code Online (Sandbox Code Playgroud)\n

并假设这些运算符的优先级是:

\n
    \n
  • 中缀 6 \xc2\xa4
  • \n
  • 中缀 7 \xe2\x8f\x86
  • \n
  • 中缀 8 \xe2\x99\x82
  • \n
\n
问题
\n

可以让GHCi这样打印出来吗?

\n
(7 \xc2\xa4 ((2 \xe2\x8f\x86 4) \xe2\x8f\x86 3)) \xc2\xa4 (1 \xe2\x99\x82 (3 \xe2\x99\x82 2))\n
Run Code Online (Sandbox Code Playgroud)\n

有没有其他方法可以快速了解该表达式的工作原理?

\n

haskell operator-precedence ghci

7
推荐指数
1
解决办法
79
查看次数

语义版本控制:更改*应该*通过库函数分配的非不透明结构

我的 C 库,在 1.0.0 版本中,定义了一个结构体和一些用于分配和使用结构体的函数:

typedef struct { int x; int y; } MyStruct;
MyStruct *allocate( int, int );
void destroy( MyStruct* );
void print( MyStruct* );
Run Code Online (Sandbox Code Playgroud)

用户不应该自己分配结构,也不应该按值复制它。这是与语义版本控制问题的主要区别:次要还是主要变化?. 例如,程序应该像这样使用它:

void f(){
  MyStruct *ms = allocate(0,0);
  ms->x += 1;
  print(ms);
  destroy(ms);
}
Run Code Online (Sandbox Code Playgroud)

现在我需要向结构中添加一个新字段,而函数的签名不会改变。

typedef struct { int x; int y; int z; } MyStruct;
Run Code Online (Sandbox Code Playgroud)

新结构比旧结构占用更多内存:如果程序尝试MyStruct直接分配实例或按值复制它,如果它链接到与构建时使用的库版本不同的库版本,则它可能会中断。

然而,这不是程序使用的方式MyStruct:只要它们遵循文档,一切正常。但是代码中没有任何内容可以阻止他们滥用结构。

我正在使用语义版本控制来对我的库进行版本控制。在上述情况下,我应该增加次要版本(以向后兼容的方式添加的功能)还是主要版本(不兼容的 API 更改)?

c semantic-versioning

6
推荐指数
1
解决办法
70
查看次数

为什么 Haskell 将类型族的卡住应用程序视为有效类型?

拿这个代码片段(在各种评论和答案提到它之后,我将类型系列更改为封闭类型):

-- a closed type family for which I won't define any instances
type family EmptyTypeFamily t where

-- 1. this type doesn't exist, since there's no `instance EmptyTypeFamily ()`
type NotAType = EmptyTypeFamily ()

-- 2. this value has a type that doesn't exist
untypedValue :: NotAType
untypedValue = undefined

main :: IO ()
main = do
  -- 3. the value with no type can be used in expressions
  return untypedValue
  return . id . fix $ \x -> …
Run Code Online (Sandbox Code Playgroud)

haskell type-families

6
推荐指数
2
解决办法
185
查看次数

递归构造一个无限的、惰性的 Monad 值

作为练习,我尝试在 monad 内递归地构造一个无限的、惰性的列表。

基本上,相当于nat,但是是一元的,monat

nat :: Int -> [Int]
nat n = n : nat (n+1)

monat :: Monad m => Int -> m [Int]
monat n = return n >>= \n -> (n:) <$> monat (n+1)
Run Code Online (Sandbox Code Playgroud)

monat并不是真正懒惰地求值:如果不计算所有元素,就不可能获得第一个元素:

ghci> take 5 $ nat 0
[0,1,2,3,4]

ghci> take 5 <$> (monat 0) :: Maybe [Int]
... never ending computation ...
Run Code Online (Sandbox Code Playgroud)

我怀疑问题一定在于fmapor >>=,因为它们是我在那里使用但不在 中使用的唯一额外函数nat。然而,我查看了他们的实现,但不明白为什么或在哪里打破了懒惰:

instance  Functor Maybe  where
    fmap _ Nothing …
Run Code Online (Sandbox Code Playgroud)

monads recursion haskell lazy-evaluation recursive-datastructures

4
推荐指数
1
解决办法
99
查看次数

从采用闭集作为输入的函数导出优化的 case-of 表达式

我有一组封闭的值:

data Value = A | B | C | D | E ...
  deriving (Eq, Ord, Show)
Run Code Online (Sandbox Code Playgroud)

以及代表它们顺序的数据结构:

order :: [[Value]]
order = [
  [ B ],
  [ A, D ],
  [ C ],
  ...
  ]
Run Code Online (Sandbox Code Playgroud)

我需要将 Value 的订单转换为Int. 我可以这样做:

prec' :: [[Value]] -> Value -> Int
prec' [] _ = 0
prec' (vs : rest) v = if v `elem` vs
  then 1 + length rest
  else prec' rest v

prec :: Value -> Int
prec …
Run Code Online (Sandbox Code Playgroud)

haskell

1
推荐指数
1
解决办法
208
查看次数