如何通过行动?

时间:2017-07-24 14:56:09

标签: haskell types

长代码示例的道歉,请参阅第30行:A.openDeviceCallback =micSpecA.OpenDeviceSpec类型对象的工厂函数。我对这个函数的参数列表不满意。

{-# OPTIONS_GHC -Wall #-}


-- Dependant on cabal packages: sdl2, wave.


module Main where


import qualified Control.Concurrent as C
--import qualified Control.Monad as M
import qualified Data.Vector.Storable.Mutable as V
import qualified Data.Set as S
import Foreign.ForeignPtr as P

import qualified SDL
import qualified SDL.Audio as A

import qualified Codec.Audio.Wave as W

import qualified System.IO as IO

import qualified Statistics.Sample as St


micSpec :: IO.Handle -> A.OpenDeviceSpec
micSpec h = A.OpenDeviceSpec {A.openDeviceFreq = A.Mandate 48000
                             ,A.openDeviceFormat = A.Mandate A.Signed16BitNativeAudio
                             ,A.openDeviceChannels = A.Mandate A.Mono
                             ,A.openDeviceSamples = 4096
                             ,A.openDeviceCallback = \_ (V.MVector size ptr) -> P.withForeignPtr ptr (\p -> IO.hPutBuf h p size)
                             ,A.openDeviceUsage = A.ForCapture
                             ,A.openDeviceName = Nothing}


waveSpec :: W.Wave
waveSpec = W.Wave {W.waveFileFormat = W.WaveVanilla
                  , W.waveSampleRate = 48000
                  , W.waveSampleFormat = W.SampleFormatPcmInt 16
                  , W.waveChannelMask = S.singleton W.SpeakerFrontCenter
                  , W.waveDataOffset = 0
                  , W.waveDataSize = 0
                  , W.waveSamplesTotal = 0
                  , W.waveOtherChunks = []}


record :: IO.Handle -> IO ()
record h = do
  SDL.initialize [SDL.InitAudio]
  (dev, _) <- A.openAudioDevice $ micSpec h
  A.setAudioDevicePlaybackState dev A.Play
--  _ <- M.forever (C.threadDelay maxBound)
  _ <- C.threadDelay 10000000
  return ()


main :: IO ()
main =  W.writeWaveFile "mic.rec" waveSpec record

在这样一个简单程序的上下文中,所需的A.OpenDeviceSpec对象是一组常量,还有一个动作。目前,该行动正在内联构建......因为这是我设法定义它的唯一方式,而不传递类型信息。

我对C的直觉是

type Cb =  A.AudioFormat t -> A.IOVector t -> IO () 
micSpec :: Cb -> A.OpenDeviceSpec

,但我无法使其发挥作用。

1 个答案:

答案 0 :(得分:0)

显然,您的问题与通用量化字段

有关
data OpenDeviceSpec = OpenDeviceSpec {
    ...
  , openDeviceCallback :: forall actualSampleType. AudioFormat actualSampleType -> IOVector actualSampleType -> IO ()
  , ...
  }

......或者,因为它可以更简单地写,

data OpenDeviceSpec = OpenDeviceSpec {
    ...
  , openDeviceCallback :: ∀ sT . AudioFormat sT -> IOVector sT -> IO ()
  , ...
  }
不用担心,这并不困难。实际上,所有标准多态函数都隐含了这样的,例如

map :: (a -> b) -> [a] -> [b]

实际上是的简写

map :: ∀ a b . (a -> b) -> [a] -> [b]

同样,要定义回调,您只需确保样本类型中的多态“。即。

openingCallback :: IO.Handle -> A.AudioFormat sT -> V.IOVector sT -> IO ()
openingCallback _ (V.MVector size ptr)
       = P.withForeignPtr ptr $ \p -> IO.hPutBuf h p size

然后

micSpec h = A.OpenDeviceSpec {
                              ...
                             ,A.openDeviceCallback = openingCallback h
                             ,...
                             }

如果您希望将openingCallback作为micSpec的参数,则需要对该参数进行普遍量化。与记录字段一样,与顶级绑定不同,必须使用显式量子声明参数是通用的:

{-# LANGUAGE Rank2Types, UnicodeSyntax #-}
micSpec :: (∀ sT . A.AudioFormat sT -> V.IOVector sT -> IO ())
               -> A.OpenDeviceSpec
micSpec openingCallback
          = A.OpenDeviceSpec {
                              ...
                             ,A.openDeviceCallback = openingCallback
                             ,...
                             }

嗯,我不确定用这种方式是否真的有意义,因为在Haskell98中甚至不合法。通用量词仍然是应该如何读取所有多态签名。