输入实例函数的推理

问题

我希望能够创建2种data typesAB并创建2个函数f

  • f :: A -> Int -> Int
  • f :: B -> String -> String -> String
  • 我能做到的唯一方法就是使用type classesinstances

    问题是,我不想明确写出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代码。 我不希望我的语言的最终用户写出明确的类型,所以我想使用Haskells强大的类型系统来尽可能地推断。

    如果我写这样的功能f2 _ s1 s2 = "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
    

    这是否甚至可以帮助您使用编译为haskell的语言,这是另一个问题。


    添加提示

    如果类型是多态的,你会看到reify给出了上面我的示例代码未涉及的内容。

     > :set -XTemplateHaskell
     > :m +IPPrint Language.Haskell.TH
     > putStrLn $(reify 'id >>= stringE . pshow)
    

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

    VarI GHC.Base.id
      (ForallT [PlainTV a_1627394484] []
         (AppT (AppT ArrowT (VarT a_1627394484)) (VarT a_1627394484)))
      Nothing
      (Fixity 9 InfixL)
    

    通过一些工作,您可以将那个类型分解到instanceD需要的CxtQ和TypeQ中。

    我不知道它产生了多少意义(a-> b)。 你可以用新的独特的变量来替换所有的类型变量,这可能是最好的Data.Generics.everywhereM,因为数据类型有许多构造函数。

    你仍然有一个问题,这是无效的:

    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
    
    链接地址: http://www.djcxy.com/p/43055.html

    上一篇: Type inference of instance functions

    下一篇: What is a type "t" in an inferred Haskell type signature?