Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/18.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/amazon-s3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何为Scala中具有一个或多个值的容器实现ADT_Scala_Algebraic Data Types_Path Dependent Type_F Bounded Polymorphism - Fatal编程技术网

如何为Scala中具有一个或多个值的容器实现ADT

如何为Scala中具有一个或多个值的容器实现ADT,scala,algebraic-data-types,path-dependent-type,f-bounded-polymorphism,Scala,Algebraic Data Types,Path Dependent Type,F Bounded Polymorphism,最后,我想实现以下目标: val onePath: One = new Log(OneLocation("root"), "foo/bar").getPath() val manyPath: Many = new Log(ManyLocation(List("base1", "base2")), "foo/bar").getPath() 为了实现这一点,似乎需要一个表示一个或多个值的AD

最后,我想实现以下目标:

  val onePath: One = new Log(OneLocation("root"), "foo/bar").getPath()
  val manyPath: Many = new Log(ManyLocation(List("base1", "base2")), "foo/bar").getPath()
为了实现这一点,似乎需要一个表示一个或多个值的ADT

这是我的实现。是否有另一种/更好/更简单的方法来实现它(我使用了路径依赖类型和F-有界类型)。是否有一个已经实现了它的库(用例似乎非常流行)

oneormy[T字符串):T
}
最后一个案例类1(a:String)扩展了一个或多个[1]{
覆盖def映射(f:String=>String):一个=一个(f(a))
}
最后一个case类Many(a:List[String])扩展了一个或多个{
覆盖defmap(f:String=>String):Many=Many(a.map(f))
}
封闭性状定位{

输入T我不确定你是否真的需要所有这些,为什么不这样做呢

@annotation.implicitNotFound(msg = "${T} is not a valid Location type.")
sealed trait Location[T] {
  def getPath(location: T, path: String): T
}

object Location {
  final def apply[T](implicit location: Location[T]): Location[T] = location

  implicit final val StringLocation: Location[String] =
    new Location[String] {
      override final def getPath(bucket: String, path: String): String =
        s"fs://${bucket}/$path"
    }
  
  implicit final val StringListLocation: Location[List[String]] =
    new Location[List[String]] {
      override final def getPath(buckets: List[String], path: String): List[String] =
        buckets.map(bucket => s"fs://${bucket}/$path")
    }
}

final class Log[L : Location](location: L, path: String) {
  def getPath(): L =
    Location[L].getPath(location, path)
}
其工作原理如下:

new Log(location = "root", "foo/bar").getPath()
// val res: String = fs://root/foo/bar

new Log(location = List("base1", "base2"), "foo/bar").getPath()
// val res: List[String] = List(fs://base1/foo/bar, fs://base2/foo/bar)

new Log(location = 10, "foo/bar").getPath()
// Compile time error: Int is not a valid Location type.

如果你真的,真的,真的想拥有所有这些课程,你可以这样做:

sealed trait OneOrMany extends Product with Serializable
final case class One(path: String) extends OneOrMany
final case class Many(paths: List[String]) extends OneOrMany

sealed trait Location extends Product with Serializable {
  type T <: OneOrMany
}

final case class OneLocation(bucket: String) extends Location {
  override final type T = One
}

final case class ManyLocations(buckets: List[String]) extends Location {
  override final type T = Many
}

@annotation.implicitNotFound(msg = "Not found a Path for Path {L}")
sealed trait Path[L <: Location] {
  def getPath(location: L, path: String): L#T
}

object Path {
  implicit final val OneLocationPath: Path[OneLocation] =
    new Path[OneLocation] {
      override final def getPath(location: OneLocation, path: String): One =
        One(path = s"fs://${location.bucket}/$path")
    }
  
  implicit final val ManyLocationsPath: Path[ManyLocations] =
    new Path[ManyLocations] {
      override final def getPath(location: ManyLocations, path: String): Many =
        Many(paths = location.buckets.map(bucket => s"fs://${bucket}/$path"))
    }
}

final class Log[L <: Location](location: L, path: String) {
  def getPath()(implicit ev: Path[L]): L#T =
    ev.getPath(location, path)
}

对我来说,删除依赖路径效果很好:

sealed trait OneOrMany[T] { self: T =>
  def map(f: String => String) : T
}
final case class One(a: String) extends OneOrMany[One] {
  override def map(f: String => String): One = One(f(a))
}
final case class Many(a: List[String]) extends OneOrMany[Many] {
  override def map(f: String => String): Many = Many(a.map(f))
}
  
sealed trait Location[+T] {
  def value: T
}
final case class OneLocation(bucket: String) extends Location[One] {
  override val value = One(bucket)
}
final case class ManyLocation(buckets: List[String]) extends Location[Many] {
  override val value = Many(buckets)
}

// the only place we require OneOrMany[T]
// to provide .map(String => String): T method
class Log[T](location: Location[OneOrMany[T]], path: String) {
  def getPath(): T = location.value.map(b => s"fs://$b/$path")
}

我将用一个类型类(
Mapper
)替换F-有界多态性

OneOrMany
Location
的实现细节,但在
Location.value.map…
中,也许我们稍微打破了封装(
Log
不仅知道
Location
,还知道
OneOrMany
,它可以映射到
OneOrMany

我会避免类型投影(
L#T
),除非它们确实是必要的(或者您有意使用它们)

这是一种类型级实现(尽管可能设计过度)

//libraryDependencies+=“com.github.dmytromitin”%%“auxify宏”%%“0.8”
导入com.github.dmytromitin.auxify.macros.{aux,instance,syntax}
导入Mapper.syntax_
导入LocMapper.syntax_
密封特性一个或多个
最后一个案例类1(a:String)扩展了一个或多个
最后一个case类Many(a:List[String])扩展了一个或多个
@语法
特征映射器[T字符串):T
}
对象映射器{
隐式valone:Mapper[one]=(t,f)=>one(f(t.a))
隐式valmany:Mapper[many]=(t,f)=>many(t.a.map(f))
}
@辅助
封闭性状定位{

受保护类型T非常感谢您提出了另一种方法。非常有趣。我发现意图不太清楚,因为它被typeclass机制的技术代码稀释了。@Yannomisan你是什么意思?为什么意图不那么清楚?你想要哪种意图?另外,你可以在现有方法中添加一个typeclass。我只是认为你正在创建许多不必要的样板类,但是如果你真的需要它们,那么混合应该很简单。我认为更容易看到有两种情况是
Location
Location
ADT。@Yannomisan你可以拥有
Location
ADT加上
Path
typeclass,我会编辑我的答案。在t之后仔细想想,我同意。最好不要向getPath方法的客户端公开
OneOrMany
(这是一个实现细节)。感谢您指出我在这里不需要抽象类型。当我想要返回给定上下文的不同类型时,我使用它的反射不好。
val onePath: One = new Log(OneLocation("root"), "foo/bar").getPath()
// val onePath: One = One(fs://root/foo/bar)

val manyPath: Many = new Log(ManyLocations(List("base1", "base2")), "foo/bar").getPath()
// val manyPath: Many = Many(List(fs://base1/foo/bar, fs://base2/foo/bar)
sealed trait OneOrMany[T] { self: T =>
  def map(f: String => String) : T
}
final case class One(a: String) extends OneOrMany[One] {
  override def map(f: String => String): One = One(f(a))
}
final case class Many(a: List[String]) extends OneOrMany[Many] {
  override def map(f: String => String): Many = Many(a.map(f))
}
  
sealed trait Location[+T] {
  def value: T
}
final case class OneLocation(bucket: String) extends Location[One] {
  override val value = One(bucket)
}
final case class ManyLocation(buckets: List[String]) extends Location[Many] {
  override val value = Many(buckets)
}

// the only place we require OneOrMany[T]
// to provide .map(String => String): T method
class Log[T](location: Location[OneOrMany[T]], path: String) {
  def getPath(): T = location.value.map(b => s"fs://$b/$path")
}
@ val onePath: One = new Log(OneLocation("root"), "foo/bar").getPath()
onePath: One = One("fs://root/foo/bar")

@ val manyPath: Many = new Log(ManyLocation(List("base1", "base2")), "foo/bar").getPath()
manyPath: Many = Many(List("fs://base1/foo/bar", "fs://base2/foo/bar"))
// libraryDependencies += "com.github.dmytromitin" %% "auxify-macros" % "0.8"
import com.github.dmytromitin.auxify.macros.{aux, instance, syntax}
import Mapper.syntax._
import LocMapper.syntax._

sealed trait OneOrMany
final case class One(a: String) extends OneOrMany
final case class Many(a: List[String]) extends OneOrMany

@syntax
trait Mapper[T <: OneOrMany] {
  def map(t: T, f: String => String) : T
}
object Mapper {
  implicit val one: Mapper[One] = (t, f) => One(f(t.a))
  implicit val many: Mapper[Many] = (t, f) => Many(t.a.map(f))
}

@aux
sealed trait Location {
  protected type T <: OneOrMany
  val value: T
}
final case class OneLocation(bucket: String) extends Location {
  override type T = One
  override val value = One(bucket)
}
final case class ManyLocation(buckets: List[String]) extends Location {
  override type T = Many
  override val value = Many(buckets)
}

@aux @instance
trait ToLocation[T <: OneOrMany] {
  type Out <: Location.Aux[T]
  def apply(t: T): Out
}
object ToLocation {
  implicit val one: Aux[One, OneLocation] = instance(t => OneLocation(t.a))
  implicit val many: Aux[Many, ManyLocation] = instance(t => ManyLocation(t.a))
}

@syntax
trait LocMapper[L <: Location] {
  def map(l: L, f: String => String): L
}
object LocMapper {
  implicit def mkLocMapper[T <: OneOrMany, L <: Location.Aux[T]](implicit
    ev: L <:< Location.Aux[T],
    m: Mapper[T],
    toLoc: ToLocation.Aux[T, L]
  ): LocMapper[L] = (l, f) => toLoc(l.value.map(f))
}

class Log[L <: Location : LocMapper](location: L, path: String) {
  def getPath(): L = location.map(b => s"fs://$b/$path")
}

val onePath: OneLocation = new Log(OneLocation("root"), "foo/bar").getPath()
// OneLocation(fs://root/foo/bar)
val manyPath: ManyLocation = new Log(ManyLocation(List("base1", "base2")), "foo/bar").getPath()
// ManyLocation(List(fs://base1/foo/bar, fs://base2/foo/bar))