2017-05-03 15 views
5

Tôi đã sau ADT:bản đồ giá trị không phải là thành viên của Chi nhánh [Int]

import cats.Functor 

sealed trait Tree[+A] 

final case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A] 

final case class Leaf[A](value: A) extends Tree[A] 

và làm thực hiện trong functor:

implicit def treeFunctor = new Functor[Tree] { 
    def map[A, B](fa: Tree[A])(f: (A) => B): Tree[B] = 
    fa match { 
     case Branch(left, right) => Branch(map(left)(f), map(right)(f)) 
     case Leaf(v) => Leaf(f(v)) 
    } 
} 

và sử dụng nó như sau:

val b = Branch(left = Branch(Branch(Leaf(5), Leaf(3)), Leaf(10)), Leaf(45)) 
val functorTree = Functor[Tree].map(b)((v) => v * 4) 

Nhưng theo dõi:

Branch(Leaf(10), Leaf(20)).map(_ * 2) 

Tôi đã có lỗi biên dịch:

Error:(21, 29) value map is not a member of A$A90.this.Branch[Int] 
Branch(Leaf(10), Leaf(20)).map(_ * 2) 
         ^

Câu hỏi của tôi là tại sao tôi nhận được thông báo lỗi?

Trả lời

3

Phương thức treeFunctor cung cấp cho bạn một phiên bản Functor[Tree] cho bất kỳ Tree, nhưng điều đó không có nghĩa là bất kỳ Tree nào sẽ tự động được chuyển đổi thành nó. Nó chỉ là cho bất kỳ Tree, bạn có một trường hợp tiềm ẩn của Functor[Tree] trong phạm vi.

Ví dụ tiềm ẩn này có phương thức map có hai tham số: ví dụ của Functor[Tree] để lập bản đồ và bản thân chức năng ánh xạ.

Vì vậy, điều này sẽ làm việc:

implicitly[Functor[Tree]].map(Branch(Leaf(10), Leaf(20)))(_ * 2) 

hoặc đơn giản là

Functor[Tree].map(Branch(Leaf(10), Leaf(20)))(_ * 2) 

(kể từ scalaz và mèo thường có sự tiện lợi này được áp dụng() cho các lớp học kiểu)

EDIT: Khi đọc câu hỏi của bạn, tôi đã bỏ lỡ phần Functor[Tree].map(b)((v) => v * 4). Vì vậy, những gì bạn đang thực sự quan tâm là phần sau.

Nếu bạn muốn giữ lại cú pháp của bạn cho cách gọi map, bạn có thể tạo một lớp ngầm như là một wrapper:

implicit class toTreeFunctor[A](tree: Tree[A]) { 
    def map[B](f: A => B) = Functor[Tree].map(tree)(f) 
} 

Branch(Leaf(10), Leaf(20)).map(_ * 2) 

Như đã chỉ ra bởi @P. Frolov, bạn có thể nhận được chuyển đổi tiềm ẩn này miễn phí từ import scalaz.syntax.functor._ hoặc import cats.syntax.functor._, nhưng lưu ý rằng bạn phải rõ ràng upcast Branch của mình để trở thành một phiên bản Tree.

+0

Loại 'val b = Branch (left = Branch (Branch (Leaf (5), Leaf (3)), Leaf (10)), Leaf (45))' là Branch [Int] và tại sao tôi có thể chuyển 'b' thành hàm' map' ẩn dụ 'Functor [Tree]'? –

+0

@zero_coding Bởi vì tại thời điểm đó bạn đang chuyển nó đến 'Functor [Tree]' trực tiếp, tương tự như 'ngầm [Functor [Tree]]' (không phải lúc nào, nhưng scalaz và mèo thường có phương thức apply() thuận tiện này cho typeclasses). Vì vậy, nó hoạt động. Nhưng bạn không thể gọi 'map' trên một' Tree', chỉ khi một 'Functor [Tree]' (và bạn đang cố gắng thực hiện trước đây bằng 'Branch (Leaf (10), Leaf (20)). _ * 2) '). Nếu bạn thêm 'toTreeFunctor', Cây của bạn sẽ tự động được chuyển đổi thành Functor [Cây] và nó sẽ là OK. – slouc

+0

@zero_coding Thành thật mà nói, tôi thậm chí không nhận thấy rằng bạn đã có phần 'Functor [Tree] .map' cho đến khi bạn đề cập đến nó trong phần bình luận :) vì vậy hãy bỏ qua phần đầu của câu trả lời của tôi (với 'ngầm ', vì nó giống với' Functor [Tree] .map') của bạn. – slouc

1

Vì bạn đang sử dụng Mèo, bạn có thể import cats.syntax.functor._ để nhận được map của mình. Lưu ý rằng bạn cần chuyển đổi nhánh thành loại cơ sở, vì Functor thể hiện được xác định cho Tree, không chỉ riêng biệt Branch. Bạn có thể sử dụng chú thích kiểu hoặc các hàm tạo thông minh trong đối tượng đồng hành để làm như vậy.

import cats.Functor 
import cats.syntax.functor._ 

sealed trait Tree[+A] 
final case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A] 
final case class Leaf[A](value: A) extends Tree[A] 

implicit def treeFunctor = new Functor[Tree] { 
    def map[A, B](fa: Tree[A])(f: (A) => B): Tree[B] = 
    fa match { 
     case Branch(left, right) => Branch(map(left)(f), map(right)(f)) 
     case Leaf(v) => Leaf(f(v)) 
    } 
} 

val tree: Tree[Int] = Branch(Leaf(10), Leaf(20)) 
tree.map(_ * 2) // Branch(Leaf(20), Leaf(40)) 

cats.syntax.functor thực sự chỉ là một đối tượng singleton mà kéo dài một đặc điểm FunctorSyntax, đóng gói một chuyển đổi ngầm từ lớp mà thực hiện Functor đến wrapper như một trong câu trả lời của slouc.Wrapper chính nó, Functor.Ops, được tạo bởi một macro Simulacrum @typeclass.

Các vấn đề liên quan