Đây là một lý do tốt cho tôi để nhìn vào Shapeless, điều mà tôi luôn muốn làm tại một số điểm :)
$ git clone [email protected]:milessabin/shapeless.git
...
$ cd shapeless
(1)
Shapeless cung cấp một số trừu tượng về tính chất và đặc biệt là biểu diễn dưới dạng danh sách không đồng nhất (HList
). Một hàm của tính chất tùy ý có thể được xem là FnHList
(một hàm lấy một đối số là HList
).
$ sbt shapeless-core/console
scala> import shapeless._
import shapeless._
scala> def isFunction[A](fun: A)(implicit fnh: FnHLister[A]) {}
isFunction: [A](fun: A)(implicit fnh: shapeless.FnHLister[A])Unit
scala> isFunction(math.sqrt _)
scala> isFunction(math.random _)
(2)
Bây giờ chúng ta hãy yêu cầu hàm trả về một Double
:
scala> def isFunReturningDouble[A](fun: A)(implicit fnh: FnHLister[A] { type Result = Double }) {}
isFunReturningDouble: [A](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double})Unit
scala> isFunReturningDouble(math.sqrt _)
scala> isFunReturningDouble(math.signum _)
<console>:12: error: could not find implicit value for parameter fnh: shapeless.FnHLister[Int => Int]{type Result = Double}
isFunReturningDouble(math.signum _)
^
(3)
Lớp LUBConstraint
loại có thể chứng kiến các giới hạn trên của các đối số danh sách:
scala> def isValidFun[A, B <: HList](fun: A)(implicit fnh: FnHLister[A] { type Result = Double; type Args = B }, lub: LUBConstraint[B, Double]) {}
isValidFun: [A, B <: shapeless.HList](fun: A)(implicit fnh: shapeless.FnHLister[A]{type Result = Double; type Args = B}, implicit lub: shapeless.LUBConstraint[B,Double])Unit
scala> isValidFun(math.random _)
scala> isValidFun((i: Int) => i.toDouble)
<console>:12: error: could not find implicit value for parameter lub: shapeless.LUBConstraint[B,Double]
isValidFun((i: Int) => i.toDouble)
^
(4)
Bây giờ chúng tôi vẫn cần phải giải nén sự tinh thần bằng cách nào đó. Ở cấp độ loại này, sẽ là Length
được cung cấp cho HList
. Để có được một giá trị thời gian chạy, một loại lớp học ToInt
là cần thiết.
Dưới đây là chức năng cuối cùng:
import shapeless._
def doubleFunArity[A, B <: HList, C <: Nat](fun: A)(implicit
fnh: FnHLister[A] { type Result = Double; type Args = B },
lub: LUBConstraint[B, Double],
len: Length[B] { type Out = C },
res: ToInt[C]
): Int = res()
Test:
scala> doubleFunArity(math.sqrt _)
res15: Int = 1
scala> doubleFunArity(math.random _)
res16: Int = 0
scala> val g: (Double, Double) => Double = math.max _
g: (Double, Double) => Double = <function2>
scala> doubleFunArity(g)
res17: Int = 2
Lưu ý rằng tiếc là nhiều math
hoạt động đang bị quá tải, và không có loại hạn chế mạnh mẽ, Scala sẽ không cung cấp cho bạn Double
phiên bản tự động, nhưng sẽ sử dụng phiên bản Int
vì một số lý do:
scala> math.max _
res18: (Int, Int) => Int = <function2>
Vì vậy, tôi cần có hướng dẫn math.max _: ((Double, Double) => Double)
để thực hiện công việc này.
Không nói rằng đây là cách tốt nhất để làm điều đó trong trường hợp cụ thể của bạn, nhưng tôi nghĩ đó là một khám phá thú vị.
Bạn nói rằng 'g' mất một n-tuple, nhưng ví dụ của bạn' Math.max' là một hàm n-ary, không phải là một 'Hàm1' lấy một tuple. Bạn nên làm rõ điều đó. –
Cố định, nắm bắt tốt – tba