我一直在使用用Haskell编写的Elm编译器.
我想开始为它实现一些优化,其中一部分涉及遍历AST并向某些节点添加"注释",例如尾调用等.
我知道我可以使用SYB或uniplate进行遍历,但我想知道是否有一种无样板的方法来处理类型.
所以,假设我们的AST有一堆代数类型:
data Expr = PlusExpr Expr Expr ...
data Def = TypeAlias String [String] Type ...
Run Code Online (Sandbox Code Playgroud)
如果我正在编写样板文件,我会创建这样的新类型:
data AnnotatedExpr = PlusExpr Expr Expr [Annotation] ...
data AnnotatedDef = TypeAlias String [String] Type [Annotation] ...
Run Code Online (Sandbox Code Playgroud)
这是很多写的boilderplate,并且似乎是避免这种做法的好习惯.
我可以这样写:
Data AnnotationTree = Leaf [Annotation]
| Internal [AnnotationTree] [Annotation]
Run Code Online (Sandbox Code Playgroud)
然后我就会有一个与AST并行运行的注释树.但是不能保证这些树具有相同的结构,因此我们失去了类型安全性.
所以我想知道,是否有一个优雅/推荐的解决方案,以避免样板,但仍然以类型安全的方式注释树?要用等效的节点替换每个节点,还有稍后将在编译中使用的注释列表?
haskell generic-programming ghc scrap-your-boilerplate template-haskell
C++的一个问题是我们从密集使用模板和模板元编程的代码中获得的可怕错误消息.这些概念旨在解决这个问题,但遗憾的是它们不会出现在下一个标准中.
我想知道,这个问题是否适用于支持通用编程的所有语言?或者C++模板出了什么问题?
不幸的是,我不知道任何其他支持泛型编程的语言(Java和C#泛型太简单了,没有C++模板那么强大).
所以我问你们:D,Ada,Eiffel模板(仿制药)是否也会产生如此丑陋的错误消息?是否可以使用具有强大通用编程范例的语言,但没有丑陋的错误消息?如果是,这些语言如何解决这个问题?
编辑:为downvoters.我真的很喜欢C++和模板.我不是说模板很糟糕.实际上我是泛型编程和模板元编程的忠实粉丝.我只是问为什么我从编译器那里收到如此丑陋的错误信息.
我想实现一些通用算法,我有很多想法,如何根据算法使用的实体的某些特征来实现专门的算法.但是,似乎我没有提出所有特殊特性,我想实现通用版本,以便它们可以与另一个专用版本一起使用.
例如,考虑distance(begin, end)(是的,我知道它在标准库中;但是,它很好很简单,可以用来演示我的问题).一般版本可能看起来像这样(我正在使用std::ptrdiff_t而不是std::iterator_traits<It>::difference_type另一种简化):
template <typename It>
auto distance(It it, It end) -> std::ptrdiff_t {
std::ptrdiff_t size{};
while (it != end) {
++it;
++size;
}
return size;
}
Run Code Online (Sandbox Code Playgroud)
当然,如果迭代器类型是随机访问迭代器,那么使用两个迭代器之间的差异来实现算法要好得多.天真地加入
template <typename It>
auto distance(It begin, It end)
-> typename std::enable_if<is_random_access_v<It>, std::ptrdiff_t>::type {
return end - begin;
}
Run Code Online (Sandbox Code Playgroud)
并不是很有效:两个实现对于随机访问迭代器都是同样好的匹配,即编译器认为它们是不明确的.处理这种情况的简单方法是将一般实现更改为仅适用于非随机访问迭代器.也就是说,SFINAE的选择使得它们相互排斥,同时也覆盖整个空间.
不幸的是,这组实现仍然是关闭的:至少在没有改变签名的情况下,我不能添加另一个实现,以防我对利用特殊属性的泛型实现有另一个想法.例如,如果我想为分段范围添加特殊处理(想法:当基础序列按原样由段组成时,例如,std::deque<...>或者std::istreambuf_iterator<cT>单独处理段的情况),则有必要将一般实现更改为仅适用当序列不是随机访问且它不是分段序列时.当然,如果我控制可以完成的实现.用户将无法扩展通用实现集.
我知道可以为特殊迭代器类型重载函数.但是,这将要求每次添加具有特殊功能的迭代器时,都需要实现相应的功能.目标是允许添加通用实现,这些实现是在它们被使用的实体暴露额外设施的情况下改进的.它类似于不同的迭代器类别,尽管属性与迭代器类别正交.
因此,我的问题是:
我已经听说所有这些关于C++ 0x的新的(on /.)不再有概念,但我不知道它们是什么?有人可以向我解释一下吗?
在Idris 效果库中,效果表示为
||| This type is parameterised by:
||| + The return type of the computation.
||| + The input resource.
||| + The computation to run on the resource given the return value.
Effect : Type
Effect = (x : Type) -> Type -> (x -> Type) -> Type
Run Code Online (Sandbox Code Playgroud)
如果我们允许资源成为值并交换前两个参数,我们得到(其余代码在Agda中)
Effect : Set -> Set
Effect R = R -> (A : Set) -> (A -> R) -> Set
Run Code Online (Sandbox Code Playgroud)
拥有一些基本的类型 - 上下文 - 会员机制
data Type : Set …Run Code Online (Sandbox Code Playgroud) 如果要从数据结构中提取元素,则必须提供其索引.但索引的含义取决于数据结构本身.
class Indexed f where
type Ix f
(!) :: f a -> Ix f -> Maybe a -- indices can be out of bounds
Run Code Online (Sandbox Code Playgroud)
例如...
列表中的元素具有数字位置.
data Nat = Z | S Nat
instance Indexed [] where
type Ix [] = Nat
[] ! _ = Nothing
(x:_) ! Z = Just x
(_:xs) ! (S n) = xs ! n
Run Code Online (Sandbox Code Playgroud)
二叉树中的元素由一系列方向标识.
data Tree a = Leaf | Node (Tree a) a (Tree a)
data TreeIx = …Run Code Online (Sandbox Code Playgroud) void在C++类型系统中是一个奇怪的疣.这是一个不完整的类型,无法完成,它有各种有关限制方式的魔术规则:
类型cv
void是不完整的类型,无法完成; 这种类型有一组空值.它用作不返回值的函数的返回类型.任何表达式都可以显式转换为cvvoid([expr.cast]).类型的表达式CVvoid应仅作为一个表达式语句,作为逗号表达式的操作数,作为一个第二或第三操作数?:([expr.cond]),作为操作数typeid,noexcept或decltype,如在表达return具有返回类型cv 的函数的语句void,或作为显式转换为cv 类型的操作数的语句void.
(N4778,[basic.fundamental]9)
除了对所有这些奇怪规则的痒感之外,由于它的使用方式有限,在编写模板时经常会出现一个痛苦的特殊情况; 通常情况下,我们希望它表现得更像std::monostate.
让我们想象一下,除了上面的引文,标准说的void是类似的东西
这是一个定义相当于以下类型的类型:
Run Code Online (Sandbox Code Playgroud)struct void { void()=default; template<typename T> explicit void(T &&) {}; // to allow cast to void };
保持void *魔法 - 可以别名任何对象,数据指针必须在往返过程中存活void *.
这个:
void"适当"类型的现有用例;return …c++ language-design generic-programming void language-lawyer
这里的想法是在大学毕业后让更好的程序员.
我想我必须使用算法,它不是你可以很容易地自己拿起的东西,我认为它使你能够在更深层次上看到软件的效率和正确性.
我也相信教授实际编程可能会有所帮助,但我学到了大部分关于工作编程的知识,这让我觉得其中一些编程课程块(不是全部)可以更好地使用.
这些结果将成为我想写给我老学校CS部门的一封信的一部分,我认为有很多课程我根本没有帮助,而我认为其他课程本来就非常宝贵.
我使用泛型相当长的时间,但我从来没有使用像List<? super T>.
这是什么意思?如何使用它?擦除后如何看待?
我也想知道:它是泛型编程(模板编程?)中的标准还是它只是一个java'发明'?例如,c#是否允许类似的结构?
根据20.8.5§1,std::less是一个具有成员函数的类模板:
template<typename T>
struct less
{
bool operator()(const T& x, const T& y) const;
// ...
};
Run Code Online (Sandbox Code Playgroud)
这意味着我必须在实例化模板时提及类型std::less<int>.为什么不是std::less具有成员函数模板的普通类呢?
struct less
{
template<typename T, typename U>
bool operator()(const T& x, const U& y) const;
// ...
};
Run Code Online (Sandbox Code Playgroud)
然后我可以简单地传递std::less给没有类型参数的算法,这可能会变得毛茸茸.
这只是出于历史原因,因为早期的编译器(据说)不能很好地支持成员函数模板(或者甚至根本不支持),或者有更深刻的东西吗?