ryb*_*ome 18 haskell types scala
在Haskell中,我认为可以以这样的方式对类型进行别名,即编译器不允许别名类型和非混淆类型之间的引用.根据这个堆栈溢出问题,可以newtype像这样使用Haskell :
newtype Feet = Feet Double
newtype Cm = Cm Double
Run Code Online (Sandbox Code Playgroud)
where Feet和Cm将表现为Double值,但尝试乘以Feet值和Cm值将导致编译器错误.
编辑:Ben在评论中指出,Haskell中的上述定义是不够的.Feet并且Cm将新的类型,其上不会有定义的功能.做了一些研究,我发现以下内容可行:
newtype Feet = Feet Double deriving (Num)
newtype Cm = Cm Double deriving (Num)
Run Code Online (Sandbox Code Playgroud)
这将创建一个从现有Num类型派生的新类型(需要使用switch :) -XGeneralizedNewtypeDeriving.当然,这些新类型会更有价值获得从其他类型,比如Show,Eq等等.但这是正确评价所需的最低Cm 7 * Cm 9.
Haskell和Scala都有type,它只是将现有类型别名化,并允许在Scala中使用无意义的代码,例如此示例:
type Feet = Double
type Cm = Double
val widthInFeet: Feet = 1.0
val widthInCm: Cm = 30.48
val nonsense = widthInFeet * widthInCm
def getWidthInFeet: Feet = widthInCm
Run Code Online (Sandbox Code Playgroud)
Scala是否具有newtype等价物,假设这样做我认为它做了什么?
Sub*_*oid 13
另一种选择是使用价值类.这些创建了一个基础类型的包装器,它在编译时转换为对原始类型的直接访问,类上的方法被转换为关联的伴随对象上的静态调用.例如:
class CM(val quant : Double) extends AnyVal {
def +(b : CM) = new CM(quant + b.quant)
def *(b : Int) = new CM(quant * b)
}
Run Code Online (Sandbox Code Playgroud)
小智 9
是的,你在scala中使用了一种称为Unboxed Tagged Types的东西.
这就是Tagged的定义方式:
type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]
Run Code Online (Sandbox Code Playgroud)
这允许你做这样的事情
sealed trait Feet
def Feet[A](a: A): A @@ Feet = Tag[A, Feet](a)
Feet: [A](a: A)scalaz.@@[A,Feet]
scala> val mass = Feet(20.0)
mass: scalaz.@@[Double,Feet] = 20.0
scala> 2 * mass
res2: Double = 40.0
Run Code Online (Sandbox Code Playgroud)
还要添加CM
sealed trait CM
def CM[A](a: A): A @@ CM = Tag[A, CM](a)
CM: [A](a: A)scalaz.@@[A,CM]
scala> val mass = CM(20.0)
mass: scalaz.@@[Double,CM] = 20.0
Run Code Online (Sandbox Code Playgroud)
如果你想将乘法限制为只有英尺,那么你可以写一个类型类型的乘法函数
trait Multiply[T] { self =>
def multiply(a: T, b: T): T
}
implicit val footInstance = new Multiply[Feet] {
def multiply(a: Feet, b: Feet): Feet = Feet(a * b)
}
implicit val cmInstance = new Multiply[CM] {
def multiply(a: CM, b: CM): CM = CM(a * b)
}
def multiply[T: Multiply](a: T, b: T): T = {
val multi = implicitly[Multiply[T]]
multi.multiply(a,b)
}
Run Code Online (Sandbox Code Playgroud)
你可以做
multiply(Feet(5), Feet(10)) // would return Feet(50)
Run Code Online (Sandbox Code Playgroud)
这是Scala能做的最好的
要了解有关盒装类型的更多信息,请查看 http://eed3si9n.com/learning-scalaz-day3
你可以使用NewTypescala-newtype库!
无耻插件:我是scala-newtype的作者
https://github.com/estatico/scala-newtype
这结合了Scalaz和Shapeless的想法以及直接从Haskell引入的想法(如GeneralizedNewTypeDeriving).
以下是使用newtype时代码的外观.我们将给出它们Feet和Cm它们自己的不同类型,并让它们Numeric基于Double(deriving自动生成)的类型派生类型类.
然后我们可以使用Numeric.Implicits- 提供的扩展方法-
object Example {
type Feet = Feet.Type
object Feet extends NewType.Default[Double] {
implicit val num: Numeric[Type] = deriving
}
type Cm = Cm.Type
object Cm extends NewType.Default[Double] {
implicit val num: Numeric[Type] = deriving
}
val widthInFeet = Feet(1.0)
val widthInCm = Cm(30.48)
import Numeric.Implicits._
// Does not compile:
// val nonsense = widthInFeet + widthInCm
// Compiles!
val doubleWidthInFeet: Feet = widthInFeet + widthInFeet
}
Run Code Online (Sandbox Code Playgroud)
但是,您*在示例中使用,我们不希望Feet * Feet = Feet它本应该是Feet * Feet = Feet²,所以让我们添加一个FeetSq类型来表示它并定义我们自己的操作比类型更安全Numeric-
object Example {
type Feet = Feet.Type
object Feet extends NewType.Default[Double] {
implicit final class Ops(val self: Feet) extends AnyVal {
def +(other: Feet) = Feet(self.repr + other.repr)
def -(other: Feet) = Feet(self.repr - other.repr)
def *(other: Feet) = FeetSq(self.repr * other.repr)
}
}
type FeetSq = FeetSq.Type
object FeetSq extends NewType.Default[Double]
type Cm = Cm.Type
object Cm extends NewType.Default[Double]
val widthInFeet = Feet(1.0)
val widthInCm = Cm(30.48)
// Does not compile:
// val nonsense = widthInFeet * widthInCm
// Compiles!
val squareFeet: FeetSq = widthInFeet * widthInFeet
}
Run Code Online (Sandbox Code Playgroud)
这里我们使用an implicit final class Ops来定义newtype上的方法.这个类在编译时被删除,所以在运行时我们最终只是从Ops对象调用扩展方法.