gpt4 book ai didi

haskell - 实例函数的类型推断

转载 作者:行者123 更新时间:2023-12-05 01:07:58 24 4
gpt4 key购买 nike

问题

我希望能够创建 2 data types :AB并创建 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 代码。
    我不希望我的语言的最终用户编写显式类型,所以我想使用 Haskell 强大的类型系统来尽可能多地进行推断。

    如果我写函数像 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

    这是否甚至可以帮助您编译为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)

    通过一些工作,您可以将该类型拆分为 CxtQ 和 TypeQ
    那个 instanceD 需要。

    我不知道生成 (a->b) 有多大意义。你可以去
    关于用新的唯一变量替换所有类型变量,这可能是最好的
    用 Data.Generics.everywhereM 之类的东西完成,因为数据类型有很多
    许多构造函数。

    您仍然会遇到无效的问题:
    instance Func_f (a -> b) where
    f _ = id

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

    这种方法可以让实例在类型不同时被选择,但在
    如果 'a' 和 'b',那么在 ghc 执行的某个稍后点,您可能会以失败告终
    不可能是一样的。
    instance (a~b) => Func_f (a->b) where
    f _ = unsafeCoerce id

    关于haskell - 实例函数的类型推断,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17703940/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com