Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/laravel/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 用于在两种类型之间转换的多态函数_Haskell_Polymorphism - Fatal编程技术网

Haskell 用于在两种类型之间转换的多态函数

Haskell 用于在两种类型之间转换的多态函数,haskell,polymorphism,Haskell,Polymorphism,是否可以使用Haskell98或扩展名编写以下函数f f :: (a -> b) -> (b -> a) -> c -> d where (c,d) :: (a,b) || (c,d) :: (b,a) 我试图创建一些通用函数来在任何两种类型a和b之间进行转换,但这两种类型都有点随意性,这使我感到困难。但是,它们是由第一个参数a->b立即设置的,所以我希望这是可能的。感谢您的指导 谢谢 [编辑] 在我看来,我的问题归结为haskell的类型系统是否支持或关键字。。

是否可以使用Haskell98或扩展名编写以下函数
f

f :: (a -> b) -> (b -> a) -> c -> d
where (c,d) :: (a,b) || (c,d) :: (b,a)
我试图创建一些通用函数来在任何两种类型
a
b
之间进行转换,但这两种类型都有点随意性,这使我感到困难。但是,它们是由第一个参数
a->b
立即设置的,所以我希望这是可能的。感谢您的指导

谢谢

[编辑]


在我看来,我的问题归结为haskell的类型系统是否支持
关键字。。。由于我的函数的直观特征是:
f:(a->b)->(b->a)->a->b或(a->b)->(b->a)->b->a

我不完全确定您希望如何使用这样的函数,但我的直觉是否定的。假设我们有:

intToString :: Int -> String
intToString = show

stringToInt :: String -> Int
stringToInt = read

foo = f intToString stringToInt
什么是
foo
类型?您必须能够将
foo
应用于
Int
String
,这在haskell中通常是不可能的:您可以将
foo
应用于其中一个,但不能同时应用于这两个


编辑:这并不是整个故事的全部--
foo
肯定可以是
foo::c->d
类型,但这将允许它应用于任何类型,而不仅仅是
Int
String
。当应用到错误的类型时,可能会抛出错误。即使这样做是可能的,但听起来也不是一个好主意。

正如alecb解释的那样,这不能按照您的签名建议的方式进行,至少不能通过普通方式进行(需要在运行时检查类型)。您可能喜欢的另一种选择是和来自
镜头
<如果您的转换函数是全向和双射的,那么code>Iso
符合要求;如果其中一个函数是部分函数,则可以使用
Prism
。下面是一个一次性的例子:

import Control.Lens
import Text.ReadMaybe

stringInt :: Prism' String Int
stringInt = prism' show readMaybe


假设我们有两个转换函数:

i2s :: Int -> String
s2i :: String -> Int
g=f i2s s2i的类型是什么?我们希望
g1
成为一个合法的表达方式,也希望
g“one”
。所以
g
接受
Int
String
。这给我们留下了三个选择:

  • g
    是一个完全多态的函数-也就是说,它的第一个参数的类型是
    a
    。因此
    g True
    g[1,2,3]
    也是合法的。如此泛型的函数与转换无关,您可以证明这类函数不能做任何有趣的事情(例如,类型为
    A->Int
    的函数必须是常量)

  • g
    需要某个类型类的参数-例如,
    show
    是一个只接受有限类型组(包括
    Int
    String
    )的函数。但是现在您的函数不是完全多态的-它只适用于这个受限类型组

  • 我们可以为此任务定义新的数据类型:

    data IntOrString = I Int | S String
    
    -- the type signature is slightly different
    f :: (Int -> String) -> (String -> Int) -> IntOrString -> IntOrString
    f i2s s2i (I n) = S $ i2s n
    f i2s s2i (S s) = I $ s2i S
    
    虽然此函数在某些情况下可能有用,但它不再通用(它支持的唯一对是
    Int
    String

  • 所以答案是-你可以用这个类型签名定义一个函数,但是它只适用于特定的类型或类型类


    编辑:不,不支持
    a->b类型的函数,其中a是Int或String
    。这需要支持子类型的动态语言或静态语言。

    我怀疑是否可以直接执行您想要的操作,但您可以使用一些变通方法:

    {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
    
    class Converter a b c where
        ($$) :: a -> b -> c
    
    data Conv a b = Conv a b
    
    instance Converter (Conv (a -> b) (b -> a)) a b where
        ($$) (Conv f _) = f
    
    instance Converter (Conv (a -> b) (b -> a)) b a where
        ($$) (Conv _ g) = g
    
    intToString :: Int -> String
    intToString = show
    
    stringToInt :: String -> Int
    stringToInt = read
    
    printString :: String -> IO ()
    printString = print
    
    printInt :: Int -> IO ()
    printInt = print
    
    main = do
        let convert = Conv intToString stringToInt
        printString $ convert $$ (12345 :: Int)
        printInt $ convert $$ "12345"
    
    我相信这已经足够接近你想要的了。唯一的区别是必须使用
    $$
    运算符,但这是不可避免的

    更新:您甚至可以删除特殊的
    Conv
    结构并使用普通元组:

    instance Converter (a -> b, b -> a) a b where
        ($$) (f, _) = f
    
    instance Converter (a -> b, b -> a) b a where
        ($$) (_, g) = g
    
    -- ...
    printString $ (intToString, stringToInt) $$ (12345 :: Int)
    printInt $ (intToString, stringToInt) $$ "12345"
    

    我认为这更接近您的要求。

    您能否详细说明您希望如何使用这样的函数?以实现更干净的代码。大多数在类型
    a->b
    之间转换的函数要么完全僵化,要么仅在一种类型上具有多态性。我可以用类型类实现后者,但我不确定这次如何实现。我想我可以使用多个参数类型类并声明
    实例ab
    ,也许…为什么?在任何给定的情况下,似乎您都知道应用转换int的方向,并且可以根据需要使用
    a2b
    b2a
    :您使用
    convertAB
    的唯一时间是您不确定在哪个方向进行转换时,这似乎是个坏主意。这与在镜头包中使用Isos非常相关。我建议您去看看,但请注意,这可能是一个非常复杂的库,需要学习。我不认为这会使代码更干净。一方面,你去掉了一个名字;另一方面,在使用函数读取代码时,需要更多的上下文来猜测函数在做什么。损失大于收益。这正是我想要的,但有两个问题:1。创建混合
    数据
    类型2。输出(
    IntOrString
    )似乎不会自动转换为原始类型:(
    Int
    String
    )。对不起,这不是一个正确的同构。
    “Hello!”
    Int
    表示形式是什么?一个方向上的对应关系只是局部的,这使得它成为一个
    棱镜
    而不是
    Iso
    @Carl事实上,让它这样做是误导性的。我将示例改为使用
    Prism
    instance Converter (a -> b, b -> a) a b where
        ($$) (f, _) = f
    
    instance Converter (a -> b, b -> a) b a where
        ($$) (_, g) = g
    
    -- ...
    printString $ (intToString, stringToInt) $$ (12345 :: Int)
    printInt $ (intToString, stringToInt) $$ "12345"