在Scala中,是否可以为类型设置别名但禁止交叉使用别名/非别名类型(如Haskell)?

ryb*_*ome 18 haskell types scala

在Haskell中,我认为可以以这样的方式对类型进行别名,即编译器不允许别名类型和非混淆类型之间的引用.根据这个堆栈溢出问题,可以newtype像这样使用Haskell :

newtype Feet = Feet Double
newtype Cm   = Cm   Double
Run Code Online (Sandbox Code Playgroud)

where FeetCm将表现为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

  • 对于简单的用例来说,这是一个非常重要的例子,其中一个人只是想要一个该死的`类型Foo = Bar`并让编译器实际上强制执行`Foo`而不是表面上. (3认同)

pyr*_*ade 5

你可以使用NewTypescala-newtype库!

无耻插件:我是scala-newtype的作者

https://github.com/estatico/scala-newtype

这结合了Scalaz和Shapeless的想法以及直接从Haskell引入的想法(如GeneralizedNewTypeDeriving).

以下是使用newtype时代码的外观.我们将给出它们FeetCm它们自己的不同类型,并让它们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对象调用扩展方法.