2017-07-24 5 views
1

Entschuldigung für das lange Codebeispiel, bitte beziehen Sie sich nur auf Zeile 30: A.openDeviceCallback =. micSpec ist eine Factory-Funktion für Objekte des Typs A.OpenDeviceSpec. Ich bin unzufrieden mit der Parameterliste dieser Funktion.Wie eine Aktion übergeben?

{-# 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 

Im Rahmen eines solchen einfachen Programm, das benötigt A.OpenDeviceSpec Objekt ist eine Reihe von Konstanten, plus eine Aktion. Momentan wird die Aktion inline erstellt ... weil ich es nur so geschafft habe, ohne Typinformationen herumzugeben.

Meine Intuition von C ist in den Linien der

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

, aber ich konnte es nicht machen.

+0

Dieser Link ist kaputt. – leftaroundabout

+0

Ich glaube nicht, dass ich die Frage vollständig verstehe. –

+0

@ ThomasM.DuBuisson Wie kann ich eine Funktion definieren Int -> IO() -> Int? Ist das möglich oder sinnvoll? – Vorac

Antwort

0

Offenbar Ihr Problem hat mit dem universell quantisierte Bereich

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

... oder, zu tun, wie es kurz mehr geschrieben werden kann,

data OpenDeviceSpec = OpenDeviceSpec { 
    ... 
    , openDeviceCallback :: ∀ sT . AudioFormat sT -> IOVector sT -> IO() 
    , ... 
    } 

Keine Sorge das ist nichts schwierig. In der Tat ist ein solcher Algorithmus implizit in allen polymorphen Standardfunktionen enthalten, z.

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

ist eine Abkürzung tatsächlich für

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

Ebenso Ihren Rückruf definieren Sie müssen nur sicherstellen, dass es polymorph in dem Probentyp. I.e.

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

und dann

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

Wenn Sie openingCallback als Argument an micSpec haben wollen, müssen Sie dieses Argument machen universell quantifiziert auch. Wie Datensatzfelder und anders als Top-Level-Bindungen, Argumente müssen mit einer expliziten Quantorenkalkül deklariert werden universell sein:

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

Nun, ich bin nicht sicher, ob es wirklich sinnvoll ist, zu setzen Es ist so, weil in Haskell98 nicht einmal legal ist. Dennoch ist der universelle Quantifizierer, wie alle polymorphen Signaturen gelesen werden sollten.

Verwandte Themen