Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.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/6/haskell/8.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 集合总数,拒绝不包括所有可能性的类型集合_Scala_Haskell_Puzzle_Type Safety_Type Constraints - Fatal编程技术网

Scala 集合总数,拒绝不包括所有可能性的类型集合

Scala 集合总数,拒绝不包括所有可能性的类型集合,scala,haskell,puzzle,type-safety,type-constraints,Scala,Haskell,Puzzle,Type Safety,Type Constraints,假设我们有以下类型: T case对象 case-object-T case-object-T 现在,如何构造T的集合,使每个子T中至少有一个出现在集合中,并在编译时强制执行此约束?与此相反,在以下情况下的集合: val s:Seq[T] = Seq(Goat) 该文件汇编如下: val ts:TotalSeq[T] = TotalSeq(Goat, Goat, Tiger) 没有。我在上面使用过Scala,但我也很乐意看到其他语言的解决方案 (如果我的话有点不对劲,请原谅;我感冒了,今天

假设我们有以下类型:

T
case对象
case-object-T
case-object-T
现在,如何构造
T
的集合,使每个子
T
中至少有一个出现在集合中,并在编译时强制执行此约束?与此相反,在以下情况下的集合:

val s:Seq[T] = Seq(Goat)
该文件汇编如下:

val ts:TotalSeq[T] = TotalSeq(Goat, Goat, Tiger)
没有。我在上面使用过Scala,但我也很乐意看到其他语言的解决方案


(如果我的话有点不对劲,请原谅;我感冒了,今天正在用拼图来逗自己。)

如果我的解释正确的话

{-# LANGUAGE GADTs #-}
{-# LANGUAGE EmptyDataDecls #-}

data Goat
data Monk
data Tiger

data T a where
    TGoat  :: T Goat
    TMonk  :: T Monk
    TTiger :: T Tiger

data TSeq a where
    TNil :: TSeq ((), (), ())
    TG   :: T Goat -> TSeq ((), m, t) -> TSeq (Goat, m, t)
    TM   :: T Monk -> TSeq (g, (), t) -> TSeq (g, Monk, t)
    TT   :: T Tiger -> TSeq (g, m, ()) -> TSeq (g, m, Tiger)
    TCG  :: T Goat -> TSeq (Goat, m, t) -> TSeq (Goat, m, t)
    TCM  :: T Monk -> TSeq (g, Monk, t) -> TSeq (g, Monk, t)
    TCT  :: T Tiger -> TSeq (g, m, Tiger) -> TSeq (g, m, Tiger)
请注意,尚未尝试使其可用。这是难以置信的笨拙

现在,考虑到这些值:

x = TCG TGoat (TG TGoat (TT TTiger (TM TMonk TNil)))

y = TG TGoat TNil
这个功能是:

f :: TSeq (Goat, Monk, Tiger) -> a
f _ = undefined
…然后只有
fx
将进行类型检查,而不是
fy

注意:从序列中删除项目留给读者作为练习


编辑:经过进一步考虑,我认为上述情况还不够糟糕。瞧:

一些神秘熟悉的样板:

data Yes = Yes
data No = No

class TypeEq' () x y b => TypeEq x y b | x y -> b
instance TypeEq' () x y b => TypeEq x y b

class TypeEq' q x y b | q x y -> b
class TypeEq'' q x y b | q x y -> b


instance TypeCast b Yes => TypeEq' () x x b
instance TypeEq'' q x y b => TypeEq' q x y b
instance TypeEq'' () x y No

class TypeCast   a b   | a -> b, b->a   where typeCast   :: a -> b
class TypeCast'  t a b | t a -> b, t b -> a where typeCast'  :: t->a->b
class TypeCast'' t a b | t a -> b, t b -> a where typeCast'' :: t->a->b
instance TypeCast'  () a b => TypeCast a b where typeCast x = typeCast' () x
instance TypeCast'' t a b => TypeCast' t a b where typeCast' = typeCast''
instance TypeCast'' () a a where typeCast'' _ x  = x
有点普通的黑客行为:

infixr 1 :*:
data NIL
data h :*: t = h :*: t

class TIns x ys r | x ys -> r
instance TIns x NIL (x :*: NIL)
instance ( TypeEq x h b 
         , TIns' b x h t r
         ) => TIns x (h :*: t) r


class TIns' b x h t r | b x h t -> r
instance TIns' Yes x h t (h :*: r)
instance (TIns x t r) => TIns' No x h t (h :*: r)


class TElem x ys r | x ys -> r
instance TElem x NIL No
instance (TypeEq x h b, TElem' b x h t r) => TElem x (h :*: t) r

class TElem' b x h t r | b x h t -> r
instance TElem' Yes x h t Yes
instance (TElem x t r) => TElem' No x h t r
aa和收益:

data TSeq2 a b where
    TNil2  :: TSeq2 NIL NIL
    TCons2 :: (TIns x ys r) => T x -> TSeq2 xs ys -> TSeq2 (x :*: xs) r

class (TElem x ys Yes) => Has ys x
instance (TElem x ys Yes) => Has ys x

class HasAll ys xs
instance HasAll ys NIL
instance (ys `Has` h, ys `HasAll` t) => HasAll ys (h :*: t)

x2 = TCons2 TGoat (TCons2 TGoat (TCons2 TTiger (TCons2 TMonk TNil2)))

y2 = TCons2 TGoat TNil2

f2 :: (s `HasAll` (Goat :*: Tiger :*: Monk :*: NIL)) => TSeq2 q s -> a
f2 _ = undefined
如上所述,
F2X2
类型检查,而
F2Y2
失败

这仍然是混乱和相当痛苦的使用,但更通用

为了证明结果在其他情况下仍然可以作为普通数据类型处理,下面是
Show
的一个实例:

instance Show (T a) where
    show TGoat = "TGoat"
    show TMonk = "TMonk"
    show TTiger = "TTiger"

instance Show (TSeq2 a b) where
    show TNil2 = "[]"
    show (TCons2 x xs) = concat [show x, ":", show xs]
现在我们可以做一些事情,比如只显示包含所有三种项目的序列:

showTotal :: (s `HasAll` (Goat :*: Tiger :*: Monk :*: NIL)) => TSeq2 q s -> String
showTotal = show

它看起来像具有通用类型约束的生成器模式:

比如:

sealed trait TBoolean
sealed trait TTrue extends TBoolean
sealed trait TFalse extends TBoolean

class SeqBuilder[HasGoat <: TBoolean, HasMonk <: TBoolean, HasTiger <: TBoolean] private (seq: Seq[T]) {

  def +=(g: Goat.type) = new SeqBuilder[TTrue, HasMonk, HasTiger](g +: seq)

  def +=(m: Monk.type) = new SeqBuilder[HasGoat, TTrue, HasTiger](m +: seq)

  def +=(t: Tiger.type) = new SeqBuilder[HasGoat, HasMonk, TTrue](t +: seq)

  def build()(implicit evg: HasGoat =:= TTrue, evm: HasMonk =:= TTrue, evt: HasTiger =:= TTrue) = seq
}


object SeqBuilder {
  def apply = new SeqBuilder(Seq())
}
object SeqBuilder {
  val all = Seq(Goat, Monk, Tiger)

  def apply(t: T*)(onAll: Seq[T] => Unit)(onViolation: => Unit) = {
     val seq = Seq(t:_*)
     if(all forall seq.contains) onAll(seq)
     else onViolation
  }
}

这样,如果没有提供所有必需的Ts,则不会调用函数onAll,否则将调用违例函数

我已将其缩减为两个子类,以使其更清晰、更短,但该原则可应用于任意数量的子类。不幸的是,对于许多子类来说,它变得相当长

sealed trait T
case class Monk extends T
case class Tiger extends T

class BaseList(val head:T, val tail:BaseList) {
  def ::(m:Monk) = new BaseList(m, this) with ListWithMonk
  def ::(t:Tiger) = new BaseList(t, this) with ListWithTiger

}
object Empty extends BaseList(null, null)

trait ListWithMonk extends BaseList {
  self:BaseList =>
  override def ::(t:Tiger) = new BaseList(t, self) with ListWithBoth
}
trait ListWithTiger extends BaseList {
  self:BaseList =>
  override def ::(m:Monk) = new BaseList(m, self) with ListWithBoth
}

trait ListWithBoth extends ListWithMonk with ListWithTiger

object Main {
  def main(args:Array[String]) {
    val l:ListWithBoth = new Monk :: new Tiger :: Empty
    val l2:ListWithBoth = new Tiger :: new Monk :: Empty
    // does not compile: val l3:ListWithBoth = new Tiger :: new Tiger :: Empty
  }

}
你不能完全按照你提出的语法来做,但是你可以接近它;您必须做的唯一折衷是使用运算符而不是
。我选择了
~
,因为在左边没有指定类型时,您没有确切地指定行为,所以我做了一些模糊合理的事情

然而,代码令人头痛。哈斯克尔更善于简明扼要地表达这一点;该策略与camccann已经提供的策略类似

object Example {
  sealed trait T
  case object Goat extends T
  case object Monk extends T
  case object Tiger extends T

  implicit def Gx(g: Goat.type) = new Goats(Seq(g))
  implicit def Mx(m: Monk.type) = new Monks(Seq(m))
  implicit def Tx(t: Tiger.type) = new Tigers(Seq(t))

  trait Combo { def s: Seq[T] }

  case class Goats(s: Seq[T]) extends Combo {
    def ~(g: Goat.type) = Goats(s :+ g)
    def ~(m: Monk.type) = GoatMonk(s :+ m)
    def ~(t: Tiger.type) = GoatTiger(s :+ t)
  }
  case class Monks(s: Seq[T]) extends Combo {
    def ~(g: Goat.type) = GoatMonk(s :+ g)
    def ~(m: Monk.type) = Monks(s :+ m)
    def ~(t: Tiger.type) = MonkTiger(s :+ t)
  }
  case class Tigers(s: Seq[T]) extends Combo {
    def ~(g: Goat.type) = GoatTiger(s :+ g)
    def ~(m: Monk.type) = MonkTiger(s :+ m)
    def ~(t: Tiger.type) = Tigers(s :+ t)
  }

  case class GoatMonk(s: Seq[T]) extends Combo {
    def ~(g: Goat.type) = GoatMonk(s :+ g)
    def ~(m: Monk.type) = GoatMonk(s :+ m)
    def ~(t: Tiger.type) = GoatMonkTiger(s :+ t)
  }
  case class GoatTiger(s: Seq[T]) extends Combo {
    def ~(g: Goat.type) = GoatTiger(s :+ g)
    def ~(m: Monk.type) = GoatMonkTiger(s :+ m)
    def ~(t: Tiger.type) = GoatTiger(s :+ t)
  }
  case class MonkTiger(s: Seq[T]) extends Combo {
    def ~(g: Goat.type) = GoatMonkTiger(s :+ g)
    def ~(m: Monk.type) = MonkTiger(s :+ m)
    def ~(t: Tiger.type) = MonkTiger(s :+ t)
  }

  case class GoatMonkTiger(s: Seq[T]) {
    def ~(t: T) = GoatMonkTiger(s :+ t)
  }

  class TotalSeq[A](val seq: Seq[A]) {}
  implicit def total_to_regular[A](ts: TotalSeq[A]) = ts.seq
  object TotalSeq {
    def apply(t: T) = Seq(t)
    def apply(co: Combo) = co.s
    def apply(gmt: GoatMonkTiger) = new TotalSeq(gmt.s)
  }
}
让我们看看如何使用这个:

scala> import Example._
import Example._

scala> val mmm = TotalSeq(Monk ~ Monk ~ Monk)
mmm: Seq[Example.T] = List(Monk, Monk, Monk)

scala> val mmm2: Seq[T] = TotalSeq(Monk ~ Monk ~ Monk)
mmm2: Seq[Example.T] = List(Monk, Monk, Monk)

scala> val mmm3: TotalSeq[T] = TotalSeq(Monk ~ Monk ~ Monk)
<console>:11: error: type mismatch;
 found   : Example.Monks
 required: Example.GoatMonkTiger
       val mmm3: TotalSeq[T] = TotalSeq(Monk ~ Monk ~ Monk)
                                                    ^

scala> TotalSeq(Tiger ~ Goat ~ Goat ~ Goat)
res5: Seq[Example.T] = List(Tiger, Goat, Goat, Goat)

scala> TotalSeq(Goat ~ Goat ~ Tiger ~ Monk)
res6: Example.TotalSeq[Example.T] = Example$TotalSeq@5568db68

scala> val gmt: TotalSeq[T] = TotalSeq(Goat ~ Goat ~ Tiger ~ Monk)
gmt: Example.TotalSeq[Example.T] = Example$TotalSeq@f312369
scala>导入示例_
导入示例_
scala>val mmm=TotalSeq(Monk~Monk~Monk)
mmm:Seq[Example.T]=列表(Monk,Monk,Monk)
scala>val mmm2:Seq[T]=TotalSeq(Monk~Monk~Monk)
mmm2:Seq[Example.T]=列表(Monk,Monk,Monk)
scala>val mmm3:TotalSeq[T]=TotalSeq(Monk~Monk~Monk)
:11:错误:类型不匹配;
发现:例如,僧侣
必填项:Example.GoatMonkTiger
val mmm3:TotalSeq[T]=TotalSeq(和尚~和尚~和尚)
^
scala>TotalSeq(老虎~山羊~山羊~山羊)
res5:Seq[Example.T]=列表(老虎、山羊、山羊、山羊)
scala>TotalSeq(山羊~山羊~老虎~僧侣)
res6:Example.TotalSeq[Example.T]=示例$TotalSeq@5568db68
scala>val gmt:TotalSeq[T]=TotalSeq(山羊~山羊~老虎~僧侣)
gmt:Example.TotalSeq[Example.T]=示例$TotalSeq@f312369

您真的想说第二个示例不应该编译吗?它至少包含每种
T
中的一种。哦,天哪,非常正确。我已经删除了这个和尚。作为一个附录,我可以想出多种方法来推广和/或改进它——这只是一个演示,而不是我在当前形式下使用的东西。实际上,我可以想出一些有用的例子。好吧,如果我认识哈斯克尔的话,我会的。@Rex Kerr:哦,绝对有可能。问题在于这种实用性是否值得增加复杂性和依赖实验性语言扩展。在这种特殊情况下,可能不会。在类型中编码简单数据有效性的方法要简单得多,但它们不符合此处问题的要求。感谢您的全面回答和对原始帖子的编辑。重点是,如果它没有至少一个僧侣、一只老虎和一只山羊,那么它将无法编译。我的解决方案允许重复,我的错误是我没有注意到你没有处理山羊。在您的方法中,您需要一个包含所有三个的列表,一个包含Mon和Goat的列表,一个包含Mon和Tiger的列表,等等……除非我弄错了,否则这个解决方案会产生大量的中间分配?为什么呢??它只是建立了不可变的列表。我很欣赏你的解决方案是如何尽可能接近原始语法的。当然,代码爆炸是一个重大问题;Scala可以很好地与某种宏系统配合使用。@troutwine-Ittay的解决方案可以很容易地进行修改,以便类似地工作。总的来说,这似乎是更好的策略(基本上使用类型系统作为宏系统)。感谢您提供简洁的解决方案和阅读材料。我确实发现我不得不用手去列举
T
的总覆盖率,这让我很苦恼,但这只需要做一次。对于终端用户来说,这不是最有吸引力的解决方案,但它非常实用。
scala> import Example._
import Example._

scala> val mmm = TotalSeq(Monk ~ Monk ~ Monk)
mmm: Seq[Example.T] = List(Monk, Monk, Monk)

scala> val mmm2: Seq[T] = TotalSeq(Monk ~ Monk ~ Monk)
mmm2: Seq[Example.T] = List(Monk, Monk, Monk)

scala> val mmm3: TotalSeq[T] = TotalSeq(Monk ~ Monk ~ Monk)
<console>:11: error: type mismatch;
 found   : Example.Monks
 required: Example.GoatMonkTiger
       val mmm3: TotalSeq[T] = TotalSeq(Monk ~ Monk ~ Monk)
                                                    ^

scala> TotalSeq(Tiger ~ Goat ~ Goat ~ Goat)
res5: Seq[Example.T] = List(Tiger, Goat, Goat, Goat)

scala> TotalSeq(Goat ~ Goat ~ Tiger ~ Monk)
res6: Example.TotalSeq[Example.T] = Example$TotalSeq@5568db68

scala> val gmt: TotalSeq[T] = TotalSeq(Goat ~ Goat ~ Tiger ~ Monk)
gmt: Example.TotalSeq[Example.T] = Example$TotalSeq@f312369