在这种情况下,是否可以折叠Applicative <*>以避免重复?

lea*_*day 2 haskell

在下面的代码中

module Main where

import Control.Monad.State
import Control.Applicative

type Code = String

toSth :: Read a => State [Code] a
toSth = state $ \(c:cs) -> ((read c), cs)

codes = ["12", "True", ""]

data Tick = Tick {n :: Int, bid :: Bool} deriving (Show)

res = runState (pure Tick <*> toSth <*> toSth) codes

main = print res
Run Code Online (Sandbox Code Playgroud)

我得到正确的结果

(Tick {n = 12, bid = True},[""])
Run Code Online (Sandbox Code Playgroud)

但是我的问题是重复

pure Tick <*> toSth <*> toSth
Run Code Online (Sandbox Code Playgroud)

即,如果记录有100个字段,那么我必须写<*> toSth100次,这看起来不像Haskell。

有没有办法来foldl<*>?我知道标准foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b在这里不起作用,因为累加器类型在每次迭代中都会改变。

非常感谢!

dan*_*iaz 7

这可以通过一些高级泛型库来完成,例如generics-sop

泛型库将数据类型与某种“统一”表示形式相互转换。这些库还提供创建或修改此类表示的功能。我们可以处理表示形式,然后再转换回原始数据类型。

{-# language DeriveGeneric, TypeApplications #-}

import qualified GHC.Generics
import           Generics.SOP (Generic,to,SOP(SOP),NS(Z),hsequence,hcpure)
import           Data.Proxy

data Tick = Tick {n :: Int, bid :: Bool} deriving (Show,GHC.Generics.Generic)

instance Generic Tick -- this Generic is from generics-sop 

res :: (Tick, [Code])
res = 
  let tickAction :: State [Code] Tick
      tickAction = to . SOP . Z <$> hsequence (hcpure (Proxy @Read) toSth)
   in runState tickAction codes
Run Code Online (Sandbox Code Playgroud)

hcpure根据有效函数(此处为)创建n元产品,该函数toSth知道如何创建产品的每个成员。我们必须传递一个Proxy约束以说服编译器。结果是每个组件都包裹在其中的产品State

hsequence就像,sequenceA但对于每个组件具有不同类型的n元产品。结果是相似的:Applicative“被向外拉”。

SOP并且Z是包装产品,让我们称之为构造函数to来获得原始的值Tick类型。

res可以被赋予此更通用的签名,以处理作为以下实例的任何单个构造函数记录Generics.SOP.Generic

{-# language DataKinds #-}

res :: (Generic r, Generics.SOP.Code r ~ '[ xs ], Generics.SOP.All Read xs) => (r,[Code])
res = 
  let tickAction = to . SOP . Z <$> hsequence (hcpure (Proxy @Read) toSth)
   in runState tickAction codes
Run Code Online (Sandbox Code Playgroud)