Haskell 如何显式指定中间变量的类型?
我有一个函数Haskell 如何显式指定中间变量的类型?,haskell,Haskell,我有一个函数readConfigFromEnv,它从环境变量中读取一些环境变量 readConfigFromEnv :: IO (Int, String) readConfigFromEnv = do portStr <- getEnv "PORT" let port = read portStr secret <- getEnv "SECRET" return (port, secret) readConfigFromEnv::IO(Int,String) rea
readConfigFromEnv
,它从环境变量中读取一些环境变量
readConfigFromEnv :: IO (Int, String)
readConfigFromEnv = do
portStr <- getEnv "PORT"
let port = read portStr
secret <- getEnv "SECRET"
return (port, secret)
readConfigFromEnv::IO(Int,String)
readConfigFromEnv=do
portStr没有ScopedTypeVariables
您可以为声明块中引入的变量添加类型签名,如do块中的let
语句,let。。。在…
表达式和where
子句中。不过,您在示例中猜到的语法并不完全正确,您需要在变量定义之外添加一个单独的类型声明(与您在其定义之外的单独一行上编写readconfigfromnv::IO(Int,String)
的方式完全相同,而不是同时作为readconfigfromnv::IO写入(Int,String)=do
)。例如,您可以编写:
readConfigFromEnv :: IO (Int, String)
readConfigFromEnv = do
portStr <- getEnv "PORT"
let port :: Int
port = read portStr
secret <- getEnv "SECRET"
return (port, secret)
这里,类型表达式x,y::a
表示x
和y
属于出现在readtwoothings
的类型签名中的a
比较删除所有a.
的和/或将ScopedTypeVariables
替换为ExplicitForAll
时发生的情况。在这种情况下,您会得到一个错误,因为a
是x
和y
类型中的a
与readtwoothings
类型中的不同。
在实践中,我发现ScopedTypeVariables
行为通常是您实际希望发生的,但因为您可以找到这样的示例,其中相同的代码意味着两种不同的事情,这取决于是否启用了ScopedTypeVariables
(您甚至可以构造代码以任何方式编译并执行不同操作的示例!),您不能盲目地打开它而不了解它如何影响类型签名
现在,有了这些背景知识,下面是我对你的问题清单的明确回答:
是如果要声明声明每个局部变量的类型,则需要ScopedTypeVariables
明确指定每个顶级定义的类型是一个非常常见的建议,我想说这几乎是Haskell社区的共识。我还发现在let
和where
绑定变量中添加类型声明很有帮助,如果它们很复杂,但这种做法不太普遍。添加类型对于没有ScopedTypeVariables
的portStr之类的东西,您可以为声明块中引入的变量添加类型签名,例如do块中的let
语句、let…in…
表达式和where
子句。不过,您在示例中猜测的语法并不完全正确,您不需要这样做o添加一个单独的类型声明来声明变量的定义(与您在其定义之外的单独一行上编写readConfigFromEnv::IO(Int,String)
的方式完全相同,而不是像readConfigFromEnv::IO(Int,String)=do那样同时编写这两个变量。因此,您可以,例如,编写:
readConfigFromEnv :: IO (Int, String)
readConfigFromEnv = do
portStr <- getEnv "PORT"
let port :: Int
port = read portStr
secret <- getEnv "SECRET"
return (port, secret)
这里,类型表达式x,y::a
表示x
和y
属于出现在readtwoothings
的类型签名中的a
比较删除所有a.
的和/或将ScopedTypeVariables
替换为ExplicitForAll
时发生的情况。在这种情况下,您会得到一个错误,因为a
是x
和y
类型中的a
与readtwoothings
类型中的不同。
在实践中,我发现ScopedTypeVariables
行为通常是您实际希望发生的,但因为您可以找到这样的示例,其中相同的代码意味着两种不同的事情,这取决于是否启用了ScopedTypeVariables
(您甚至可以构造代码以任何方式编译并执行不同操作的示例!),您不能盲目地打开它而不了解它如何影响类型签名
现在,有了这些背景知识,下面是我对你的问题清单的明确回答:
是如果要声明声明每个局部变量的类型,则需要ScopedTypeVariables
明确指定每个顶级定义的类型是一个非常常见的建议,我想说这几乎是Haskell社区的共识。我还发现在let
和where
绑定变量中添加类型声明很有帮助,如果它们很复杂,但这种做法不太普遍。添加类型对于像portStr这样的事情,您可以在portStr中指定类型,例如portStr
。注意:我上面评论的最后一个例子中的歧义也可以通过TypeApplications
解决,也就是说x还要添加一件事情,您可以指定类型,例如portStr
在portStr中注意:我上面评论的最后一个例子中的模糊性也可以通过TypeApplications
解决,也就是说x要添加两条注释:使用readIO
而不是read
在格式错误的输入上立即强制崩溃,而不是在端口
是第一个实际使用的;您总是可以将类型签名移动到另一端,如let port=read portStr::Int
(或者端口谢谢@DanielWagner。在某些情况下,我的Monad是一个类型类,而不是concreate类型,我可以将类型签名放在另一侧。例如,如果我使用MonadIO或ReaderT,我只知道Monad将返回字符串,但我不知道Monad具体是什么
{-# LANGUAGE ScopedTypeVariables #-}
readTwoThings :: forall a. Read a => IO (a, a)
readTwoThings = do
sx <- readLn
sy <- readLn
let x, y :: a
x = read sx
y = read sy
pure (x, y)