虽然许多人称赞不变性,但我发现在主流编程中难以维持.根据我的经验,程序员迟早会使字段再次变异,以避免重构大部分代码,这些代码必须传递更新的对象以及返回值.
Scala对复制构造函数有一些支持,但我知道没有令人满意的解决方案来更新复杂的对象结构.我可能错过了什么.
我实验过的最好的实现是Haskell中的数据镜头.但是,Haskell很难学.流行的跨平台编程语言(如Java或Scala)有哪些选择?
如果我有一个带镜头的记录类型,是否可以在不使用基础记录访问器的情况下构建新记录?
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Lens.TH
data Foo = Foo { _s :: String
, _b :: Bool
} deriving (Show, Eq)
makeLenses ''Foo
Run Code Online (Sandbox Code Playgroud)
我可以制作Foo
一个实例,Data.Default
然后def
用镜头修改,但并非所有记录类型都有合理的默认值.Control.Lens有自己的方法吗?
在Clojure中我们有这个identity
功能.它使用如下:
user=> (filter identity [1 2 3 nil 4 false true 1234])
(1 2 3 4 true 1234)
user=> (partition-by identity (sort "abcdaabccc"))
((\a \a \a) (\b \b) (\c \c \c \c) (\d))
Run Code Online (Sandbox Code Playgroud)
从我在Haskell中看到的内容 - id
在使用镜头时使用,并用于其他更高阶的功能.
我的问题是(除了明显的类型系统的差异)是否identity
Clojure中的函数具有相同用途和目的为id
在Haskell功能?
为什么我要问的是当我在Clojure中查看以下Lens的示例时- 我们看到的Id
定义如下functor
:
(defprotocol Functor
(fmap [functor f] "fmap :: f a -> (a -> b) -> f b"))
;; data …
Run Code Online (Sandbox Code Playgroud) 有没有办法在Swift中为类中的属性自动生成getter/setter函数对?在Haskell中沿着镜头的线条.
我已经能够手动执行以下操作:
class PropertyLens<U, T> {
let getter: U -> T
let setter: (U, T) -> ()
init(getter: (U -> T), setter: ((U, T) -> ())) {
self.getter = getter
self.setter = setter
}
func get(u: U) -> T {
return getter(u)
}
func set(u: U, t: T) {
setter(u, t)
}
}
// ...
let myPropertyLens = PropertyLens<MyClass, Int>(getter: { $0.myProperty }, setter: { $0.myProperty = $1 })
Run Code Online (Sandbox Code Playgroud)
然而,这比我想要的更冗长,乏味,更容易出错.是否有我缺少的内置功能?
我想将这行代码转换map (^?! ix 0) [[0, 1], [4, 5], [9, 1]]
成完全使用镜头,所以像[[0, 1], [4, 5], [9, 1]] & each . ix 0
.但是,类型不匹配.这样做的正确方法是什么?
我有一个嵌套的case
类结构List
为简单起见,请使用以下示例 -
case class Address(street: String, city: String, state: String, zipCode: Int)
case class Person(firstName: String, lastName: String, addresses: List[Address])
case class Department(people: List[Person])
Run Code Online (Sandbox Code Playgroud)
说有List[Department]
; 现在,如果我想List[Department]
通过过滤Address
每个Person
没有特定zipCode
值的新东西来创建一个新的; 传统上我们可以做到以下
def filterPersonsByAddress(dlist: List[Department]): List[Department] = {
dlist.map { d =>
val people = d.people.map { p =>
p.copy(addresses = p.addresses.filterNot(_.zipCode == 38978))}
d.copy(people=people)
}
}
Run Code Online (Sandbox Code Playgroud)
这种方法不具备性能,因为取决于嵌套级别,它可以是Big O(n ^ 2)或Big O(n ^ 3);
我试图通过Monocle学习镜头.到目前为止,我学到的是当你必须"修改"一个深层嵌套的case
类结构但尚未找到一种方法根据条件"切断"嵌套结构的某些部分并返回一个新结构时,镜头很有用.这可能是通过Monocle吗?此外,我不确定Lenses是否能够帮助实现更好的Big O时间?
我有以下型号
import monocle.macros.Lenses
import monocle.function.all._
import monocle.std.list._
@Lenses("_") case class Poll(pollChoices: List[PollChoice], totalVoteCount: Int)
@Lenses("_") case class PollChoice(id: Int, value: Int, percentage: Int)
Run Code Online (Sandbox Code Playgroud)
我想要实现的是通过更新pollChoices列表中每个元素的所有百分比属性来更新Poll的pollChoices属性.我的问题是,新的百分比值是基于价值的属性PollChoice和totalValueCount中的投票.
到目前为止我所做的是:
val poll = Poll(List(PollChoice(1,3,0), PollChoice(2,5,0)), 8)
(Poll._pollChoices composeTraversal each composeLens PollChoice._percentage)
.modify(oldPercentage => oldPercentage + 1)(poll)
Run Code Online (Sandbox Code Playgroud)
但是这样我只能访问我在修改步骤中更新的元素的百分比属性,我也需要该值.
我假设我需要使用Traversable来进行这种修改,但我不确定如何.谢谢.
在功能光学中,一个表现良好的棱镜(在scala中称为部分透镜,我相信)应该具有类型的集函数'subpart -> 'parent -> 'parent
,其中如果棱镜"成功"并且在结构上与'parent
给定的参数兼容,则它返回'parent
给定适当的子部分,修改为'subpart
给定值.如果棱镜"失败"并且在结构上与'parent
参数不相容,那么它将返回'parent
给定的未修改.
我想知道为什么棱镜不返回'parent option
(Maybe
对于Haskellers)来表示set函数的通过/失败性质?程序员是否应该能够从返回类型中判断该集合是否"成功"?
我知道在功能光学领域已经进行了大量的研究和思考,所以我肯定必须有一个我似乎无法找到的确定答案.
(我来自F#背景,所以如果我使用的语法对Haskell或Scala程序员来说有点不透明,我很抱歉).
甲棱镜为聚焦到联产品类型的光学部件,而仿射遍历是一种光学器件的可聚焦于0 1的元件,即,AffineTraversal s t a b
是同构的(s -> Maybe a, (s, b) -> t)
.据我所知,如果镜头由棱镜组成,我们会得到一个仿射遍历,只要使用适当的棱镜编码.
我感兴趣的是将Maybe
那个(幼稚)配方移动到设置器侧而不是吸气器侧,这样我就可以使用一个总是提取一个元素的光学元件,但可能无法将它放回去.
我的用例与细化类型有关.想象一下,我们有一个类型A
及其细化B
(B ? A
).然后有一个棱镜refined :: Prism' A B
:一个A
可能或可能不是有效的B
,但每个B
都可以re
进入一个A
.结合了Lens' C A
有refined
,我们有一个仿射遍历.在另一个方向上,可以想象一个光学元件unrefined
比一个光学元件更聪明re refined
:如果它是有效的,A
可以变成一个光学元件,或者如果它不是,则可以变成光学元件.现在,如果我们结合了有,我们有我们的双仿射穿越:它可以随时获得从,但放回任何旧的可能违反的不变和产量,而不是.可以以类似的方式确保更复杂的不变量.Just b
B
Nothing
Lens' C B
unrefined
A
C
A
C
Nothing
Just c
有趣的是,Scala 的单片机 …
爱德华·克梅特(Edward Kmett)的光学图书馆;Control.Lens定义了大量类型。
其中大多数具有相对自我解释的名称,例如Traversal和Fold。
它还定义了一些名称不那么明显的类型,例如Bazaar
在“义卖市场”页面上:
aka索引的笛卡尔商店comonad,索引的Kleene商店comonad或索引的FunList。
...
通常,集市上有很多商店,您可以轻松添加更多。
我无法弄清楚Market类型名称背后的原因。我认为这在某种程度上也与商店monads / comonads有关?它是否正确?
lenses ×10
haskell ×6
scala ×4
haskell-lens ×2
clojure ×1
f# ×1
identity ×1
immutability ×1
java ×1
monocle ×1
swift ×1
terminology ×1