2012-01-30 38 views
13

Tôi nghĩ PartialFunction có thể là Monoid. Quy trình suy nghĩ của tôi có đúng không? Ví dụ:Scala PartialFunction có thể là Monoid?

import scalaz._ 
import scala.{PartialFunction => -->} 

implicit def partialFunctionSemigroup[A,B]:Semigroup[A-->B] = new Semigroup[A-->B]{ 
    def append(s1: A-->B, s2: => A-->B): A-->B = s1.orElse(s2) 
} 

implicit def partialFunctionZero[A,B]:Zero[A-->B] = new Zero[A-->B]{ 
    val zero = new (A-->B){ 
    def isDefinedAt(a:A) = false 
    def apply(a:A) = sys.error("error") 
    } 
} 

Nhưng phiên bản hiện tại Scalaz (6.0.4) không được bao gồm. Có lý do gì đó không bao gồm không?

+0

tôi giả sử bạn nhận thức được rằng 'Function1' là một monoid dưới thành phần? –

+1

@dcsobral 'Hàm1 [A, A]', hay còn gọi là 'Endo [A] ', là. – retronym

Trả lời

28

Hãy tỏa sáng một ánh sáng khác về điều này.

PartialFunction[A, B] là đẳng cấu cho A => Option[B]. (Trên thực tế, để có thể kiểm tra nếu nó được định nghĩa cho một A đưa ra mà không gây ra đánh giá của B, bạn sẽ cần A => LazyOption[B])

Vì vậy, nếu chúng ta có thể tìm thấy một Monoid[A => Option[B]] chúng tôi đã chứng minh khẳng định của bạn.

Với Monoid[Z], chúng ta có thể hình thành Monoid[A => Z] như sau:

implicit def readerMonoid[Z: Monoid] = new Monoid[A => Z] { 
    def zero = (a: A) => Monoid[Z].zero 
    def append(f1: A => Z, f2: => A => Z) = (a: A) => Monoid[Z].append(f1(a), f2(a)) 
} 

Vì vậy, những gì monoid (s) làm chúng ta phải nếu chúng ta sử dụng Option[B] như Z của chúng tôi? Scalaz cung cấp ba. Ví dụ chính yêu cầu Semigroup[B].

implicit def optionMonoid[B: Semigroup] = new Monoid[Option[B]] { 
    def zero = None 
    def append(o1: Option[B], o2: => Option[B]) = o1 match { 
    case Some(b1) => o2 match { 
     case Some(b2) => Some(Semigroup[B].append(b1, b2))) 
     case None => Some(b1) 
    case None => o2 match { 
     case Some(b2) => Some(b2) 
     case None => None 
    } 
    } 
} 

Sử dụng này:

scala> Monoid[Option[Int]].append(Some(1), Some(2)) 
res9: Option[Int] = Some(3) 

Nhưng đó không phải là cách duy nhất để kết hợp hai Options. Thay vì phụ thêm nội dung của hai tùy chọn trong trường hợp chúng là cả hai Some, chúng ta có thể chỉ cần chọn đầu tiên hoặc cuối cùng của hai tùy chọn. Hai kích hoạt này, chúng tôi tạo ra một loại riêng biệt với thủ thuật được gọi là Tagged Types. Điều này tương tự với tinh thần của Haskell là newtype.

scala> import Tags._ 
import Tags._ 

scala> Monoid[Option[Int] @@ First].append(Tag(Some(1)), Tag(Some(2))) 
res10: [email protected]@[Option[Int],scalaz.Tags.First] = Some(1) 

scala> Monoid[Option[Int] @@ Last].append(Tag(Some(1)), Tag(Some(2))) 
res11: [email protected]@[Option[Int],scalaz.Tags.Last] = Some(2) 

Option[A] @@ First, nối thông qua nó Monoid, sử dụng cùng một orElse ngữ nghĩa như ví dụ của bạn.

Vì vậy, đặt này tất cả cùng nhau:

scala> Monoid[A => Option[B] @@ First] 
res12: scalaz.Monoid[A => [email protected]@[Option[B],scalaz.Tags.First]] = 
     [email protected] 
+0

Cảm ơn rất nhiều! Tôi đã không nhận ra rằng PartialFunction là isomorphic để A => LazyOption [B] –

+0

Cảm ơn, @retronym! Các loại Tagged chỉ có sẵn trong scalaz-7, cho phiên bản trước, nó cần thiết để sử dụng đặc điểm FirstOption, tôi có đúng không? – lester

+2

@ lester yep, chính xác. Các loại được gắn thẻ có một vài cạnh sắc nét, thật không may, chúng tôi có thể cần hỗ trợ scalac tốt hơn trước khi chúng tôi đề xuất chúng. Ví dụ là: 'List (Tag (1))' đưa ra một 'ClassCastException' là một phần của trình biên dịch xử lý các đối số như một mảng đối tượng, và một phần sau là một mảng nguyên thủy. – retronym

2

Không, điều này có vẻ tốt, đáp ứng cả hai yêu cầu cho (không giao hoán) Monoid. Ý tưởng thú vị. Bạn đang cố gắng hỗ trợ trường hợp sử dụng nào?

+0

Rất tiếc, danh tính bị vi phạm rõ ràng. –

+1

@Heiko Rất tiếc, nhưng tuyên bố của bạn rõ ràng là sai. Ngay cả khi câu trả lời là sai nó là xa rõ ràng (ít nhất là với tôi). – ziggystar

0

Số không của bạn chắc chắn vi phạm tiên đề cho phần tử nhận dạng, nhưng tôi cho rằng chức năng nhận dạng (một phần) sẽ không sao.

Phụ lục của bạn cũng không đáp ứng các luật Monoid, nhưng thay vì hoặcElse bạn có thể gọi vàSau (sáng tác). Nhưng điều này sẽ chỉ hoạt động cho A == B:

implicit def partialFunctionSemigroup[A]: Semigroup[A --> A] = new Semigroup[A --> A] { 
    def append(s1: A --> A, s2: => A --> A): A-->A = s1 andThen s2 
} 

implicit def partialFunctionZero[A]: Zero[A --> A] = new Zero[A --> A] { 
    val zero = new (A --> A) { 
    def isDefinedAt(a:A) = true 
    def apply(a:A) = a 
    } 
} 
+0

Bạn có thể đưa ra ví dụ ngược lại không? – ziggystar

+0

Luật bản sắc: ea = a = ae –

+0

'E' và' a' nào bị vi phạm? – ziggystar