简体   繁体   English

共享在函数式编程语言的实现中引用了什么

[英]What does sharing refer to in the implementation of a functional programming language

Sharing means that temporary data is stored if it is going to be used multiple times. 共享意味着如果临时数据将被多次使用,则将其存储。 That is, a function evaluates it's arguments only once. 也就是说,函数只评估它的参数一次。 An example would be: 一个例子是:

let x = sin x in x * x

What other features contribute to sharing and how would they interact with the need for practical programs to perform IO? 还有哪些其他功能有助于共享 ,以及它们如何与实际执行IO的需求相互作用?

Sharing is about a kind of equality: pointer equality. 共享是一种平等:指针平等。 In Haskell's value land (semantic interpretation) there is no such thing as sharing. 在Haskell的价值土地(语义解释)中,没有共享这样的东西。 Values can only be equal if they have instances of Eq and then "equality" is defined to be the binary relation (==) . 如果它们具有Eq实例,则值只能相等,然后将“相等”定义为二元关系(==)

Sharing thus escapes the semantic interpretation by referring to this underlying pointer equality which exists by virtue of implementation instead of semantics. 因此,共享通过引用由于实现而不是语义而存在的底层指针相等来逃避语义解释。 This is a useful side effect sometimes, though. 不过,这有时会产生副作用。 Unfortunately, since Haskell is defined by its semantic interpretation, use of sharing is undefined behavior. 不幸的是,由于Haskell是由其语义解释定义的,因此共享的使用是未定义的行为。 It's tied to a particular implementation. 它与特定的实现有关。 A few libraries use sharing and thus have behavior tied to GHC. 一些图书馆使用共享,因此行为与GHC相关联。

There is one built-in sharing mechanism, though. 但是,有一种内置的共享机制。 This is exposed by the StableName interface. 这由StableName接口公开。 We generate StableName a objects using makeStableName :: a -> IO (StableName a) and have an instance Eq (StableName a) —thus StableName introduces some kind of equality for any type. 我们使用makeStableName :: a -> IO (StableName a)生成StableName a对象,并且有一个instance Eq (StableName a) StableName任何类型引入了某种相等性。

StableName equality is almost pointer equality. StableName相等几乎是指针相等。 To quote the Haddock documentation 引用Haddock文档

If sn1 :: StableName and sn2 :: StableName and sn1 == sn2 then sn1 and sn2 were created by calls to makeStableName on the same object. 如果sn1 :: StableNamesn2 :: StableNamesn1 == sn2那么sn1sn2是通过对同一对象调用makeStableName创建的。

Note that this is just an if statement, not an if and only if . 请注意,这只是一个if语句,而不是if和only if The fact that two things can be "pointer equivalent" but still have different stable names sometimes is (a) forced by the flexibility Haskell leaves to the GC and (b) a loophole that allows StableName s to exist in any Haskell implementation even if there's no such thing as "pointer equality" in the implementation whatsoever. 事实上两个东西可以是“指针等价”,但仍然有不同的稳定名称有时是(a)强迫Haskell留给GC的灵活性和(b)允许StableName存在于任何Haskell实现中的漏洞,即使有在实现中没有“指针等式”这样的东西。

These StableName s still have no semantic meaning, but since they're introduced in the IO monad that's "OK". 这些StableName仍然没有语义含义,但是因为它们在IO monad中被引入了“OK”。 Instead, they might be said to implement an (ironically) unstable subset of the smallest (most specific) equality relation possible on any type. 相反,它们可能被认为是在任何类型上实现可能的最小(最具体)等式关系的(具有讽刺意味的)不稳定子集。

The clearest example of sharing in functional programming comes from Clean, which is based on graph rewriting. 函数式编程中最明显的共享示例来自Clean,它基于图形重写。 There, a computation refers to a DAG, so we can view the expression (sin x) * (sin x) as 在那里,计算是指DAG,因此我们可以将表达式(sin x) * (sin x)视为

    (*)
  /     \
sin x   sin x

Graph rewriting systems have an explicit notion of sharing, so we could express that computation as 图重写系统有一个明确的共享概念,所以我们可以将计算表达为

   (*)
   / \
   \ /
  sin x

pointing the multiplication node to the same node, thereby sharing the computation of sin x . 将乘法节点指向同一节点,从而共享sin x的计算。 Term rewriting systems do not have so explicit a notion of sharing, but the optimization is still relevant. 术语重写系统没有如此明确的共享概念,但优化仍然是相关的。 In GHC, one can sometimes express sharing with local variables, eg rewriting 在GHC中,有时可以表达与局部变量的共享,例如重写

f x = (sin x) * (sin x)

into

f x = sinx * sinx
  where sinx = sin x

But since the two are semantically equivalent, the compiler is free to implement both the same way, with or without sharing. 但由于这两者在语义上是等价的,因此编译器可以自由地以相同的方式实现,无论是否共享。 By my understanding, the GHC will generally preserve sharing introduced with local variables and sometimes introduce it (adding sharing to the first), but with no formal expression of sharing in term rewriting systems either behaviour is implementation dependent (see tel's comment and answer). 根据我的理解,GHC通常会保留与局部变量一起引入的共享,有时会引入它(向第一个添加共享),但是在术语重写系统中没有正式的共享表达,要么行为依赖于实现(参见tel的评论和回答)。

Sharing touches IO because side-effecting values cannot be shared. 共享触摸IO,因为无法共享副作用值。 If we consider an impure language, there is a difference between 如果我们考虑一种不纯的语言,那么它们之间就存在差异

(string-append (read-line)
               (read-line))

and

(let ((s (read-line)))
  (string-append s s))

The first executes the IO action twice, requesting two lines from the user and appending them; 第一个执行IO动作两次,从用户请求两行并附加它们; the second "shares" the IO action, executing it once and appending it to itself. 第二个“共享”IO动作,执行一次并将其附加到自身。 In general, sharing a pure computation reduces execution time without changing the result of the program, while sharing a side-effecting value (one that may change over time, or interacts with the user) changes the result. 通常,共享纯计算可以在不改变程序结果的情况下减少执行时间,同时共享副作用值(可能随时间变化或与用户交互的值)会改变结果。 For the compiler to automatically share computations, it needs to know that they are pure, and thus that reducing the number of evaluations does not matter. 为了让编译器自动共享计算,它需要知道它们是纯粹的,因此减少评估的数量并不重要。 Clean does this with uniqueness types; Clean以独特的类型做到这一点; an IO action has the type attribute UNQ, which tells the compiler that it should not be shared. IO操作具有类型属性UNQ,它告诉编译器不应该共享它。 Haskell does the same somewhat differently with the IO monad. Haskell对IO monad的做法有所不同。

Your example is not an example of sharing -- it just multiplies a value with itself (and then throws the original value away). 您的示例不是共享的示例 - 它只是将值与自身相乘(然后将原始值抛出)。

Sharing is the case where some part of a data structure occurs twice in a larger data structure, or in different data structures. 共享是指数据结构的某些部分在较大的数据结构或不同的数据结构中出现两次的情况。 For example: 例如:

p  = (1, 2)
pp = (p, p)  -- p is shared between the first and second component of pp

xs = [1, 2, 3]
ys = 0::1::xs
zs = 5::xs  -- ys and zs share the same tail

In memory, such sharing will result in a DAG structure. 在内存中,这种共享将导致DAG结构。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 “世界”在函数式编程世界中意味着什么? - What does the “world” mean in functional programming world? “纯粹”在“纯函数式语言”中意味着什么? - What does “pure” mean in “pure functional language”? 在选择与LLVM一起使用的函数式编程语言时,有哪些权衡取舍? - When choosing a functional programming language for use with LLVM, what are the trade-offs? 纯函数式编程语言中的双重链接列表 - Doubly Linked List in a Purely Functional Programming Language 读取文件并使用haskell访问(功能编程语言) - reading file and accessing in haskell (functional programming language) 功能编程范例是否有可视化建模语言或风格? - Is there a visual modeling language or style for the functional programming paradigm? 函数式编程中的代数结构是什么? - What are algebraic structures in functional programming? 纯函数式编程中的“价值”是什么? - What is “value” in pure functional programming? 这种函数式编程优化叫什么? - What is this functional programming optimization called? 函数式编程:如何处理函数式编程中的异常或其等价物 - Functional Programming: How to handle exceptions in Functional Programming or what is its equivalent
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM