一个Scala中最强大的模式是充实,我的图书馆*模式,它采用隐式转换为出现添加方法,以现有的类,而不需要动态方法解析.例如,如果我们希望所有字符串都有spaces
计算他们有多少个空格字符的方法,我们可以:
class SpaceCounter(s: String) {
def spaces = s.count(_.isWhitespace)
}
implicit def string_counts_spaces(s: String) = new SpaceCounter(s)
scala> "How many spaces do I have?".spaces
res1: Int = 5
Run Code Online (Sandbox Code Playgroud)
不幸的是,这种模式在处理泛型集合时遇到了麻烦.例如,已经询问了许多关于按顺序对项目进行分组的问题.没有内置的东西可以一次性工作,所以这似乎是使用泛型集合C
和泛型元素类型的rich-my-library模式的理想候选者A
:
class SequentiallyGroupingCollection[A, C[A] <: Seq[A]](ca: C[A]) {
def groupIdentical: C[C[A]] = {
if (ca.isEmpty) C.empty[C[A]]
else {
val first = ca.head
val (same,rest) = ca.span(_ == first)
same +: (new SequentiallyGroupingCollection(rest)).groupIdentical
}
}
}
Run Code Online (Sandbox Code Playgroud)
当然,除了它不起作用.REPL告诉我们:
<console>:12: error: not found: …
Run Code Online (Sandbox Code Playgroud) 我几乎总是有一个或两个Scala REPL会话,这使得Java或Scala类很容易进行快速测试.但是,如果我更改一个类并重新编译它,REPL将继续加载旧的类.有没有办法让它重新加载类,而不是必须重新启动REPL?
举一个具体的例子,假设我们有Test.scala文件:
object Test { def hello = "Hello World" }
Run Code Online (Sandbox Code Playgroud)
我们编译它并启动REPL:
~/pkg/scala-2.8.0.Beta1-prerelease$ bin/scala
Welcome to Scala version 2.8.0.Beta1-prerelease
(Java HotSpot(TM) Server VM, Java 1.6.0_16).
Type in expressions to have them evaluated.
Type :help for more information.
scala> Test.hello
res0: java.lang.String = Hello World
Run Code Online (Sandbox Code Playgroud)
然后我们将源文件更改为
object Test {
def hello = "Hello World"
def goodbye = "Goodbye, Cruel World"
}
Run Code Online (Sandbox Code Playgroud)
但是我们不能用它:
scala> Test.goodbye
<console>:5: error: value goodbye is not a member of object Test
Test.goodbye
^
scala> import Test; …
Run Code Online (Sandbox Code Playgroud) 专业化有望为原始类型提供高效率的实现,只需极少的额外样板.但专业化似乎过于渴望自己的利益.如果我想专门化一个类或方法,
def foo[@specialized(Byte) A](a: A): String = ???
class Bar[@specialized(Int) B] {
var b: B = ???
def baz: B = ???
}
Run Code Online (Sandbox Code Playgroud)
那么我需要编写一个涵盖专用和通用案例的实现.如果这些情况彼此真的不同,那么实现不会重叠怎么办?例如,如果我想在字节上执行数学运算,我需要& 0xFF
在逻辑中插入一堆s.
我可以编写一个专门的类型类来正确地进行数学运算,但是这不会只是将同一个问题推回一个级别吗?如何+
以不与更一般的实现冲突的方式为该类型类编写专用方法?
class Adder[@specialized(Byte) A] {
def +(a1: A, a2: A): A = ???
}
Run Code Online (Sandbox Code Playgroud)
此外,一旦我以这种方式创建一个类类,我如何确保正确的类型类用于我的专用方法而不是通用版本(如果它是真正的通用,应该可能编译,当然会运行,除了它不是我想要的)?
有没有办法在没有宏的情况下做到这一点?使用宏更容易吗?
该java.lang.Double.parseValue
方法以不一致的方式处理奇怪的双打表示.
如果你写了一个非常大的数字,那么它超出了double
范围,但是然后附加一个大的负指数使它回到范围内,你最终在范围内(这里用Scala的REPL说明):
scala>
java.lang.Double.parseDouble("10000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000001e-400")
res25: Double = 1.0E-21
Run Code Online (Sandbox Code Playgroud)
另一方面,如果你写一个非常小的数字,这么小,它超出了double
范围,但是然后使用一个大的正指数将它带回范围内,它只有在指数本身不是太大时才有效:
scala>
java.lang.Double.parseDouble("0.000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000001e400")
res26: Double = Infinity
scala>
java.lang.Double.parseDouble("0.000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000001e200")
res27: Double = 1.0E-179
Run Code Online (Sandbox Code Playgroud)
这只是一个错误,或者某个地方是否存在允许这种行为的规范,或者规范允许所有这些都失败了,当得到正确的结果时,应该感谢一个人的祝福?(如果它是一个错误,它已被修复?)
(旁白:我正在编写自定义的字符串到双重代码,并且会针对棘手案例推迟Java默认实现,但此测试用例失败.)
有没有办法依赖特征中case类中定义的方法?例如,复制:以下不起作用.不过,我不知道为什么.
trait K[T <: K[T]] {
val x: String
val y: String
def m: T = copy(x = "hello")
def copy(x: String = this.x, y: String = this.y): T
}
case class L(val x: String, val y: String) extends K[L]
Run Code Online (Sandbox Code Playgroud)
得到:
error: class L needs to be abstract, since method copy in trait K of type
(x: String,y: String)L is not defined
case class L(val x: String, val y: String) extends K[L]
^
Run Code Online (Sandbox Code Playgroud) 假设有人想建立一个新的通用类,Novel[A]
.这个类将包含许多有用的方法 - 也许它是一种集合 - 因此你想要将它子类化.但是您希望方法返回子类的类型,而不是原始类型.在Scala 2.8中,为了使该类的方法返回相关的子类而不是原始类,必须做的最小工作量是多少?例如,
class Novel[A] /* What goes here? */ {
/* Must you have stuff here? */
def reverse/* What goes here instead of :Novel[A]? */ = //...
def revrev/*?*/ = reverse.reverse
}
class ShortStory[A] extends Novel[A] /* What goes here? */ {
override def reverse: /*?*/ = //...
}
val ss = new ShortStory[String]
val ss2 = ss.revrev // Type had better be ShortStory[String], not Novel[String]
Run Code Online (Sandbox Code Playgroud)
如果你想要Novel
协变,这个最小量会改变吗?
(2.8这些集合除了其他内容之外还会执行此操作,但它们也会以更加花哨(和有用)的方式使用返回类型 - 问题是如果一个人只想要这个子类型 - …
collections scala scala-2.8 higher-kinded-types enrich-my-library
我正在查看JVM字节码指令,并惊讶地发现类之间的所有交互(例如,转换new
等)依赖于对其他类的标识的常量池查找.
我是否正确地推断这意味着一个班级不能知道超过64k其他人的存在,因为不可能引用它们?如果确实需要引用那么多,那么应该做什么 - 将工作委托给多个类,每个类都可以有自己的<64k交互?
(我感兴趣的原因是我习惯于编写代码生成器,有时会生成数千个不同的类,并且某些语言(例如Scala)会大量创建类.所以看来如果是真的我必须要小心:如果我在一个类中有数百个方法,每个方法使用数百个(不同的)类,我可以超过常量池空间.)
Scala 2.10引入了值类,您可以通过使类扩展来指定它们AnyVal
.值类有许多限制,但它们的一个巨大优点是它们允许扩展方法而没有创建新类的惩罚:除非需要装箱,例如将值类放在数组中,它只是旧类加上一组将类作为第一个参数的方法.从而,
implicit class Foo(val i: Int) extends AnyVal {
def +*(j: Int) = i + j*j
}
Run Code Online (Sandbox Code Playgroud)
解开一些不比写i + j*j
自己更贵的东西(一旦JVM内联方法调用).
不幸的是,SIP-15中描述值类的一个限制是
- C的基础类型可能不是值类.
如果你有一个值类,你可以开始,比如说,作为一种提供类型安全单位而不需要拳击开销的方法(除非你真的需要它):
class Meter(val meters: Double) extends AnyVal {
def centimeters = meters*100.0 // No longer type-safe
def +(m: Meter) = new Meter(meters+m.meters) // Only works with Meter!
}
Run Code Online (Sandbox Code Playgroud)
那么有没有办法在Meter
没有对象创建开销的情况下进行充实?SIP-15的限制防止了明显的限制
implicit class RichMeter(m: Meter) extends AnyVal { ... }
Run Code Online (Sandbox Code Playgroud)
做法.
所以我一直试图通过定义一个更高阶的函子来将我的函子的直觉推到极限,即a,将一阶类型作为类型参数的F,以及函数和提升第一阶类型的函数到scala中的这个更高的上下文喜欢
trait Functor1[F[_[_]] {
def hmap[X[_], Y[_]] : (X ~> Y) => F[X] => F[Y]
}
Run Code Online (Sandbox Code Playgroud)
我一直试图定义普通仿函数的一些地图可导函数,例如
trait Functor[F[_]] {
def map[A, B] : (A => B) => F[A] => F[B]
// there's a few more besides this that are map derivable
def distribute[A,B](fab: F[(A, B)]): (F[A], F[B])
}
Run Code Online (Sandbox Code Playgroud)
但我不能写任何类型的支票...我只是在玩,但我想知道是否还有其他人在这条路上走得比我聪明
可以在scala中定义更高阶的仿函数吗?如果不是那么在哈斯克尔?
我最近一直在关注各种scala日志库,其中绝大多数都实现了日志记录功能
def debug(s: => String)
Run Code Online (Sandbox Code Playgroud)
因此,如果关闭调试日志记录,它将不会执行该语句.但是,我刚刚遇到了logula,它特别指出了它的一个好处
对于其记录语句,这意味着两两件事:不像很多Scala的记录库,Logula不使用通过按姓名语义(=>阿例如,f):
- Scala编译器不必为每个日志记录语句创建一次性闭包对象.这应该减少垃圾收集压力.
这实际上对我来说是完全合理的.所以我的问题是,是否存在比较这两种方法的真实世界性能基准/数据?理想情况下,从现场项目到人为的基准测试?
scala ×8
collections ×2
java ×2
bytecode ×1
callbyname ×1
case-class ×1
double ×1
haskell ×1
jvm ×1
logging ×1
parsing ×1
performance ×1
scala-2.8 ×1
value-class ×1