Haskell 实例函数的类型推断

Haskell 实例函数的类型推断,haskell,types,ghc,type-inference,Haskell,Types,Ghc,Type Inference,问题 我希望能够创建2个数据类型:A和B,并创建2个函数f: f::A->Int->Int f::B->String->String->String 我能做到这一点的唯一方法(据我所知)是使用类型类和实例 问题是,我不想显式写入f签名-我希望类型检查器为我推断它。可能吗 示例代码 {-# LANGUAGE FlexibleInstances, FunctionalDependencies, UndecidableInstances #-} data A = A{ax::Int} deriv

问题

我希望能够创建2个
数据类型
A
B
,并创建2个函数
f

  • f::A->Int->Int
  • f::B->String->String->String
我能做到这一点的唯一方法(据我所知)是使用
类型类
实例

问题是,我不想显式写入
f
签名-我希望类型检查器为我推断它。可能吗

示例代码

{-# LANGUAGE FlexibleInstances, FunctionalDependencies, UndecidableInstances #-}

data A = A{ax::Int} deriving(Show)
data B = B{bx::Int} deriving(Show)
data C = C{cx::Int} deriving(Show)

-- I don't want to explicit say the signature is Int->Int
-- I would love to write: 
-- instance Func_f A (a->b) where 
instance Func_f A (Int->Int) where 
    f _ i = i*2

-- I don't want to explicit say the signature is String->String->String
-- I would love to write:
-- instance Func_f B (a->b->c) where 
instance Func_f B (String->String->String) where 
    f _ s1 s2 = "test"++s1++s2

-- I don't want to explicit say the signature is a->a
-- I would love to write:
-- instance Func_f C (a->b) where 
instance Func_f C (a->a) where 
    f _ i = i

class Func_f a b | a -> b  where
    f :: a -> b

f2 _ s1 s2 = "test"++s1++s2 -- Here the type inferencer automaticly recognizes the signature

main :: IO ()
main = do 
    let 
        a = A 1
        b = B 2
        c = C 3
        a_out = f a 5
        b_out = f b "a" "b"
        c_out = c 6

    print a_out
    print b_out
    print c_out
解释

我正在编写自定义域语言编译器,并由此生成Haskell代码。 我不希望我的语言的最终用户编写显式类型,所以我希望使用Haskell强大的类型系统尽可能多地推断

如果我编写像
f2_s1s2=“test”++s1++s2
这样的函数,我必须显式地编写它的签名,因为编译器可以推断它。在上面的例子中,我们是否可以要求编译器推断
f
的签名


我很想知道解决这个问题的每一个可能的“黑客”,即使这个黑客是“丑陋的”,因为我正在生成Haskell代码,它不必是“漂亮的”。

这是一种有效的方法。如果您的fA fB有类型,则需要涵盖更多的案例 变量的推断类型。在这种情况下,以下代码将具有 某些模式匹配在编译时失败

{-# LANGUAGE FlexibleInstances, FunctionalDependencies, TemplateHaskell #-}

import Language.Haskell.TH

data A = A{ax::Int} deriving(Show)
data B = B{bx::Int} deriving(Show)

fA A{} i = i*(2 :: Int)

fB B{} s1 s2 = "test"++s1++s2

class Func_f a b | a -> b  where
    f :: a -> b

let
    getLetter (AppT (AppT _ x) _) = x
    getSnd (AppT x y) = y
    mkInst name0  = do
        (VarI n ty _ _) <- reify name0
        fmap (:[]) $ instanceD (return [])
            [t| Func_f
                    $(return $ getLetter ty)
                    $(return $ getSnd ty) |]
            [valD (varP 'f) (normalB (varE name0)) []]

    in fmap concat $ mapM mkInst ['fB, 'fA]


main :: IO ()
main = do 
    let 
        a = A 1
        b = B 2
        a_out = f a 5
        b_out = f b "a" "b"

    print a_out
    print b_out
打印出描述(a->a)的内容:

通过一点工作,您可以将该类型拆分为CxtQ和TypeQ 这就是我们需要的

我不知道生成(a->b)有多大意义。你可以去 关于用新的唯一变量替换所有类型变量,这可能是最好的 使用类似Data.Generics.everywhere的内容完成,因为数据类型有许多 许多建设者

您仍然会遇到一个问题,即这是无效的:

instance Func_f (a -> b) where
   f _ = id
这种获取(a->b)的方法会使程序崩溃:

instance Func_f (a -> b) where
   f _ = unsafeCoerce id
这种方法允许在类型不同时选择实例,但至少 在ghc的执行过程中,如果出现“a”和“b”,则可能会以失败告终 不可能是一样的

instance (a~b) => Func_f (a->b) where
   f _ = unsafeCoerce id

据我所知,无法从类的给定实现推断类型类参数的类型。这可能很困难,因为可以推断第一个示例的类型为
f::(numa)=>a->a->a
f
的任何实现都会使用第一个参数,还是仅仅用于确定使用哪个版本的
f
?@sabauma-我知道-但我真的不想推断非常详细的类型,如
Int->Int
-如果Haskell将其推断为
f:(Num a),我会很高兴的=>A->A->A
或其他东西-如果我以后可以使用它(如果可能的话)。有时会使用第一个参数-您可以用命令式语言将其视为“this”。@sabauma-我编辑了这个示例-请查看注释。@danillo,也许您可以提出一些类似于如何实现的方法。感谢您提供了这个示例-它工作得非常好。你能告诉我,是否有可能以某种方式使用此技术来支持非严格类型的函数,如
faa{}i=i
?我编辑了问题-添加了
C
数据类型和相关实例,以更好地显示问题。如果有可能将您的解决方案与诸如
faa{}i=i
之类的函数一起使用,这将是我的救命稻草:)我已经添加了更多的提示,以使C(或您原来的A)被接受:模板haskell不以令人愉快著称,因此我留给您编写该代码的练习;)
instance Func_f (a -> b) where
   f _ = unsafeCoerce id
instance (a~b) => Func_f (a->b) where
   f _ = unsafeCoerce id