Woj*_*ilo 4 haskell types type-inference ghc
问题
我希望能够创建2 data types
:A
和B
创建2个函数f
:
f :: A -> Int -> Int
f :: B -> String -> String -> String
我能做到的唯一方法(就我所知)是使用type classes
和instances
.
问题是,我不想明确写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
Run Code Online (Sandbox Code Playgroud)
释
我正在编写自定义域语言编译器,因此我正在生成Haskell代码.我不希望我的语言的最终用户写出显式类型,所以我想使用Haskells强大的类型系统来尽可能地推断.
如果我写功能就像f2 _ s1 s2 = "test"++s1++s2
我不是要明确写入其签名-因为编译器可以推断它.我们可以以某种方式要求编译器推断f
上述示例中的签名吗?
我很想知道解决这个问题的每一个可能的"hack",即使这个hack会"丑陋",因为我正在生成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
Run Code Online (Sandbox Code Playgroud)
这是否有助于你使用你正在编译的语言来帮助你使用haskell是另一个问题.
添加了提示
如果类型是多态的,你会看到reify给出了我上面的示例代码未涵盖的内容.
> :set -XTemplateHaskell
> :m +IPPrint Language.Haskell.TH
> putStrLn $(reify 'id >>= stringE . pshow)
Run Code Online (Sandbox Code Playgroud)
打印出描述的内容(a-> a):
VarI GHC.Base.id
(ForallT [PlainTV a_1627394484] []
(AppT (AppT ArrowT (VarT a_1627394484)) (VarT a_1627394484)))
Nothing
(Fixity 9 InfixL)
Run Code Online (Sandbox Code Playgroud)
通过一些工作,您可以将那里的Type拆分为instanceD需要的CxtQ和TypeQ.
我不知道它产生了多少感觉(a-> b).您可以使用新的唯一变量替换所有类型变量,这可能最好用Data.Generics.everywhereM来完成,因为数据类型有许多构造函数.
你仍然会遇到一个无效的问题:
instance Func_f (a -> b) where
f _ = id
Run Code Online (Sandbox Code Playgroud)
获取(a-> b)的方法可能会导致程序崩溃:
instance Func_f (a -> b) where
f _ = unsafeCoerce id
Run Code Online (Sandbox Code Playgroud)
这种方法允许在类型不同时选择实例,但是在ghc执行的某个稍后阶段,如果'a'和'b'不能相同,则最终会失败.
instance (a~b) => Func_f (a->b) where
f _ = unsafeCoerce id
Run Code Online (Sandbox Code Playgroud)