2012-05-27 26 views
13

Có cách nào loại an toàn để viết một hàmbiến Generic của bi fab = (fa, fb)

bi f a b = (f a, f b) 

như vậy mà nó sẽ có thể sử dụng nó như thế này:

x1 :: (Integer, Char) 
x1 = bi head [2,3] "45" 

x2 :: (Integer, Char) 
x2 = bi fst (2,'3') ('4',5) 

x3 :: (Integer, Double) 
x3 = bi (1+) 2 3.45 

? Trong rank-n-loại ví dụ luôn có một cái gì đó đơn giản hơn nhiều như

g :: (forall a. a -> a) -> a -> a -> (a, a) 
g f a b = (f a, f b) 
+0

Tôi không nghĩ vậy. Bạn sẽ phải định lượng nó trên tất cả các kiểu đầu vào, đòi hỏi phải trừu tượng hóa các ràng buộc về kiểu chữ. –

+0

@LouisWasserman, có một số công cụ mới (ConstraintKinds) cho phép trừu tượng hóa các ràng buộc. – aemxdp

+0

Được rồi, tôi thấy điều đó, nhưng tôi không nghĩ rằng bạn có thể định lượng các loại kết quả như bạn phải làm. Nếu nó có dạng «a -> a', bạn có thể làm' bi :: (cxt a, cxt b) => (forall x. Cxt x => x -> x) -> a -> b -> (a, b) ', nhưng tôi không nghĩ rằng bạn có thể tự động nhận được" loại chức năng "từ mỗi đầu vào đến loại kết quả của nó. –

Trả lời

4

Vâng, mặc dù không phải trong Haskell. Nhưng bậc cao lambda calculus đa hình (hay còn gọi là hệ thống F-omega) là tổng quát hơn:

bi : forall m n a b. (forall a. m a -> n a) -> m a -> m b -> (n a, n b) 
bi {m} {n} {a} {b} f x y = (f {a} x, f {b} y) 

x1 : (Integer, Char) 
x1 = bi {\a. List a} {\a. a} {Integer} {Char} head [2,3] "45" 

x2 : (Integer, Char) 
x2 = bi {\a . exists b. (a, b)} {\a. a} {Integer} {Char} (\{a}. \p. unpack<b,x>=p in fst {a} {b} x) (pack<Char, (2,'3')>) (pack<Integer, ('4',5)>) 

x3 : (Integer, Double) 
x3 = bi {\a. a} {\a. a} {Integer} {Double} (1+) 2 3.45 

Ở đây, tôi viết f {T} cho các ứng dụng kiểu tường minh và giả định một thư viện gõ tương ứng. Một cái gì đó như \a. a là một loại lambda cấp. Ví dụ x2 phức tạp hơn, bởi vì nó cũng cần các kiểu tồn tại để "quên" các bit đa hình khác trong các đối số.

Bạn thực sự có thể mô phỏng này trong Haskell bằng cách định nghĩa một newtype hoặc datatype cho mỗi khác nhau m hoặc n bạn nhanh chóng với, và thông qua chức năng bao bọc một cách thích hợp f mà thêm và loại bỏ nhà xây dựng cho phù hợp. Nhưng rõ ràng, đó không phải là niềm vui chút nào.

Chỉnh sửa: Tôi phải chỉ ra rằng điều này vẫn không phải là giải pháp tổng thể đầy đủ. Ví dụ: tôi không thể thấy cách bạn có thể nhập

swap (x,y) = (y,x) 
x4 = bi swap (3, "hi") (True, 3.1) 

ngay cả trong Hệ thống F-omega.Vấn đề là chức năng swap là đa hình hơn bi cho phép, và không giống như với x2, kích thước đa hình khác không bị lãng quên trong kết quả, do đó, thủ thuật tồn tại không hoạt động. Có vẻ như bạn sẽ cần đa hình loại để cho phép một (để đối số để bi có thể được đa hình trên một số lượng khác nhau của các loại).

3

Ngay cả với ConstraintKinds, tôi nghĩ rằng rào cản sẽ được định lượng so với "loại chức năng" từ các đối số kết quả. Những gì bạn muốn là dành cho f để bản đồ a -> bc -> d và để mất a -> b -> (c, d), nhưng tôi không nghĩ có cách nào để định lượng mối quan hệ đó với toàn bộ tính tổng quát.

Một số trường hợp đặc biệt có thể thực hiện được, mặc dù:

(forall x . cxt x => x -> f x) -> a -> b -> (f a, f b) 
-- e.g. return 

(forall x . cxt x => f x -> x) -> f a -> f b -> (a, b) 
-- e.g. snd 
(forall x . cxt x => x -> x) -> a -> b -> (a, b) 
-- e.g. (+1) 

nhưng cho rằng bạn đang cố gắng để định lượng qua chức năng loại nhiều hay ít tùy ý, tôi không chắc chắn bạn có thể làm cho công việc đó.

+0

Có vẻ như bạn đang đúng về trường hợp chung, cảm ơn. – aemxdp

2

Đây là khoảng gần như là bạn sẽ nhận được, tôi nghĩ rằng:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-} 
module Data.Function.Bi (bi, Fn(..)) 

bi :: (Fn i a a', Fn i b b') => i -> a -> b -> (a', b') 
bi i a b = (fn i a, fn i b) 

class Fn i x x' | i x -> x' where 
     fn :: i -> x -> x' 

Sử dụng nó như vậy:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, RankNTypes, 
      FlexibleInstances, UndecidableInstances #-} 
import Data.Function.Bi 

data Snd = Snd 

instance Fn Snd (a, b) b where 
     fn Snd = snd 

myExpr1 :: (Int, String) 
myExpr1 = bi Snd (1, 2) ("a", "b") 
-- myExpr == (2, "b") 

data Plus = Plus (forall a. (Num a) => a) 

instance (Num a) => Fn Plus a a where 
     fn (Plus n) = (+n) 

myExpr2 :: (Int, Double) 
myExpr2 = bi (Plus 1) (1, 2) (1.3, 5.7) 
-- myExpr2 == (3, 6.7) 

Nó rất phiền phức, nhưng như chung càng tốt.

+0

Có một số lỗi trong mã này. Đầu tiên, một thể hiện của 'Fn' lấy ba đối số, không phải là trường hợp trong' Fn (Plus a) '. Thứ hai, loại 'Plus' là' * 'nên' Thêm a' không hợp lệ. Cuối cùng, bạn cần 'FlexibleInstances' vì biến kiểu' b' xuất hiện hai lần trong thể hiện 'Snd'. – is7s

+0

@ is7s Đã sửa, xin lỗi! –

+0

Bí quyết thú vị, cảm ơn. – aemxdp

6
{-# LANGUAGE TemplateHaskell #-} 

bi f = [| \a b -> ($f a, $f b)|] 

 

ghci> :set -XTemplateHaskell 
ghci> $(bi [|head|]) [2,3] "45" 
(2,'4') 

;)

+0

upvote cho giá trị hài: P –

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