Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/batch-file/6.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 - Fatal编程技术网

Haskell 在组合类型类的函数时,如何添加中间值的类型注释?

Haskell 在组合类型类的函数时,如何添加中间值的类型注释?,haskell,Haskell,背景: 我正在开发一个声明式编译器。在本课程中,我将编写一个类来构造中间数据结构。构建数据结构后,可以从数据结构中重新定义输出。为了简化stackoverflow,我创建了以下代码: module Main where import qualified Data.Word as W import qualified Octetable as Oct main :: IO () main = do print (buildNRender "123"

背景: 我正在开发一个声明式编译器。在本课程中,我将编写一个类来构造中间数据结构。构建数据结构后,可以从数据结构中重新定义输出。为了简化stackoverflow,我创建了以下代码:

module Main where

import qualified Data.Word as W
import qualified Octetable as Oct

main :: IO ()
main = 
    do
        print (buildNRender "123")

data MyData = MyData Integer

data Construction model = Contains model | Error String
    deriving Show

class Builder g where
    build :: String -> (Construction g)
    render :: (Construction g) -> [W.Word8]
    buildNRender :: String -> [W.Word8]
    buildNRender = render . build

instance Builder MyData where
    build s = Contains (MyData (read s :: Integer))
    render (Contains (MyData n)) = Oct.toOctets n
    render (Error _) = []
明显的问题是,“buildNRender”不能是Builder的一部分,因为类型参数g根本没有使用

现在,对我来说很明显,类型类不能像这样工作,其中两个或多个函数组合中的中间值有一个类型参数

下面的代码使中间类型显式,并且可以工作-但是没有buildNRender

...

main :: IO ()
main = 
    do
        print (render ((build "123") :: (Construction MyData))

...
但是,是否有一种优雅的方法来定义类的默认方法(如“buildNRender”),并在调用方的上下文中指定中间类型,如下面的代码

...

main :: IO ()
main = 
    do
        print ((buildNRender "123") :: ?(Construction MyData)?)

...
明显的问题是,
buildNRender
不能是
Builder
的一部分,因为根本不使用类型参数
g

嗯,这曾经是一个问题(具体来说,
g
将是不明确的),但现在不是了,因为GHC现在有了允许使用此类参数的扩展

{-# LANGUAGE AllowAmbiguousTypes, TypeApplications, ScopedTypeVariables, UnicodeSyntax #-}
    
module Main where

import qualified Data.Word as W
import qualified Octetable as Oct

main :: IO ()
main = 
    do
        print (buildNRender @MyData "123")

data MyData = MyData Integer

data Construction model = Contains model | Error String
    deriving Show

class Builder g where
    build :: String -> (Construction g)
    render :: (Construction g) -> [W.Word8]

buildNRender :: ∀ g . Builder g => String -> [W.Word8]
-- ∀ (forall) g . introduces the type variable g into scope
-- needs extension AllowAmbiguousTypes
buildNRender = render . build @g -- @g is a Type Application

instance Builder MyData where
    build s = Contains (MyData (read s :: Integer))
    render (Contains (MyData n)) = Oct.toOctets n
    render (Error _) = []

AllowAmbiguousTypes
TypeApplications
现在绝对是一个好方法。但是,如果您希望避免使用它们,可以使用两种经典技术之一:代理传递或newtype标记

代理传递 使用
ScopedTypeVariables
最简单:

class Builder g其中
build::String->Construction g
渲染:构造g->[W.Word8]
buildNRender::proxy g->String->[W.Word8]
buildNRender=渲染。(build::String->Construction g)
类标题中的
g
类型变量将作用于
buildNRender
默认定义的主体,并可在中用于解决歧义。比如说,

buildNRender (Proxy :: Proxy MyData) "123"
传递给
buildNRender
的代理参数可以是其最后一个类型参数表示所需类型的任何类型。规范选择在
Data.Proxy
中定义:

data Proxy a = Proxy
如果您也希望避免
ScopedTypeVariables
,那么您需要某种帮助函数。例如,你可以写

blub :: proxy g -> (String -> Construction g) -> String -> Construction g
blub _ = id
然后

  buildNRender p = render . blub p build
如果您需要很多这类东西,您可以定义更通用的版本<例如,code>blub的类型可以用前缀符号重写:

blub :: proxy g -> (->) String (Construction g) -> (->) String (Construction g)
blub _ = id
这导致了一种普遍性

blub :: proxy g -> f (c g) -> f (c g)
blub _ = id
可以用完全相同的方式使用

新类型标记 为了避免代理传递的任何可能的运行时影响,在处理过程中会产生大量麻烦,您可以导入
Data.taged
,它定义

newtype Tagged s b = Tagged {unTagged :: b}
-- It has a Functor instance
现在您可以编写(使用
ScopedTypeVariables

如果没有
ScopedTypeVariables
,事情会变得更加棘手。一个选择是

blurble :: f (c g) -> Tagged g (f (c g))
blurble = Tagged

  buildNRender :: Tagged g (String -> [W.Word8])
  buildNRender = (render .) <$> blurble build

哇,这正是我想要的。谢谢大家!@LeftRoundound:如果你不介意的话,我会编辑你的代码以适应原始代码,并提供一些注释以显示需要哪个扩展的部分。请随意编辑。(如果我不喜欢它,我总是可以回滚…@leftroundout:我看不出有什么理由让
buildNRender
成为
Builder
类的成员。但是,因为我知道还有其他解决方案,
buildNRender
是类
Builder
的成员,所以说
buildNRender
不能是所有
的此解决方案的成员是否正确?或者这可能吗?背景:当我尝试时,我得到一个错误(“无法推断(生成器g1)”)。。。。因此,一个示例调用可以是
buildNRender(Proxy::Proxy Int)
。但这也需要GHC扩展?为了避免任何错误,我们必须定义一个变量,比如
intProxy::Proxy Int;intProxy=Proxy
要传递给blub
?这也有点烦人…@意志,不,不需要扩展。仅当我们想要捕获类型参数时才需要作用域类型变量
Int
在任何地方都可用。我指的是内联类型签名。我想它需要延期。@dfeur,谢谢。我要一个接一个地去理解它。这需要一些时间。我喜欢代理传递,其中buildNRender可以属于类。如果您不介意的话,我添加了一个示例代码。@dfeur,我想没有特殊的理由来命名函数
blub
,是吗?如果没有,为了更好的可读性,
applyProxy
是否会使用一个适合该用途的名称?
  buildNRender :: Tagged g (String -> [W.Word8])
  buildNRender = Tagged (render . (build :: String -> Construction g))
blurble :: f (c g) -> Tagged g (f (c g))
blurble = Tagged

  buildNRender :: Tagged g (String -> [W.Word8])
  buildNRender = (render .) <$> blurble build
unTagged (buildNRender :: Tg.Tagged MyData (String -> [W.Word8])) "123"