2013-05-25 29 views
5

Tại sao chức năng này có các loại: deleteAllMp4sExcluding :: [Char] -> IO (IO()) thay vì deleteAllMp4sExcluding :: [Char] -> IO()Tại sao có một đơn vị IO lồng nhau, IO (IO()), làm giá trị trả về của hàm của tôi?

Ngoài ra, làm thế nào tôi có thể viết lại này để nó sẽ có một định nghĩa đơn giản hơn?

Dưới đây là định nghĩa hàm:

import System.FilePath.Glob 
import qualified Data.String.Utils as S 

deleteAllMp4sExcluding videoFileName = 
    let dirGlob = globDir [compile "*"] "." 
     f = filter (\s -> S.endswith ".mp4" s && (/=) videoFileName s) . head . fst 
     lst = f <$> dirGlob 
    in mapM_ removeFile <$> lst 

Trả lời

16

<$> khi áp dụng cho IO s đã gõ (a -> b) -> IO a -> IO b. Vì vậy, kể từ mapM_ removeFile có loại [FilePath] -> IO(), b trong trường hợp này là IO(), do đó loại kết quả trở thành IO (IO()).

Để tránh làm tổ như thế này, bạn không nên sử dụng <$> khi hàm bạn đang cố áp dụng tạo ra giá trị IO. Thay vào đó, bạn nên sử dụng >>= hoặc nếu bạn không muốn thay đổi thứ tự của các toán hạng, =<<.

+0

cảm giác trực giác của mình, nó giúp: 'removeFile' thêm 1 hiệu ứng.nếu chúng ta chỉ muốn 1 hiệu ứng cuối cùng, chúng ta phải cho ăn nếu có thứ gì đó có hiệu lực 0. 'lst' đã có hiệu lực rồi. vì vậy chúng ta cần loại bỏ nó trước tiên, bằng cách sử dụng liên kết (giai đoạn tính toán) chạy hiệu ứng để lấy giá trị với hiệu ứng 0 – nicolas

8

Riffing on answer của sepp2k, đây là một ví dụ tuyệt vời để hiển thị sự khác biệt giữa FunctorMonad.

Các Haskell độ nét tiêu chuẩn của Monad đi một cái gì đó như thế này (giản thể):

class Monad m where 
    return :: a -> m a 
    (>>=) :: m a -> (a -> m b) -> m b 

Tuy nhiên, đây không phải là cách duy nhất các lớp có thể đã được xác định. Một thay thế chạy như thế này:

class Functor m => Monad m where 
    return :: a -> m a 
    join :: m (m a) -> m a 

Cho rằng, bạn có thể xác định >>= về fmapjoin:

(>>=) :: Monad m => m a -> (a -> m b) -> m b 
ma >>= f = join (f <$> ma) 

Chúng tôi sẽ xem xét điều này trong một phác thảo đơn giản của vấn đề bạn chay vao. Những gì bạn đang làm có thể được biểu đồ hóa như thế này:

ma  :: IO a 
f  :: a -> IO b 
f <$> ma :: IO (IO b) 

Bây giờ bạn đã bị mắc kẹt bởi vì bạn cần một IO b, và lớp Functor không có hoạt động đó sẽ giúp bạn có từ IO (IO b). Cách duy nhất để có được nơi bạn muốn là để nhúng vào Monad, và các hoạt động join chính xác là những gì giải quyết nó:

join (f <$> ma) :: IO b 

Nhưng theo định nghĩa join/<$> của >>=, đây là giống như:

ma >>= f :: IO a 

Lưu ý rằng thư viện Control.Monad đi kèm với phiên bản join (được viết bằng return(>>=)); bạn có thể đặt nó vào chức năng của bạn để có được kết quả mong muốn. Nhưng điều tốt hơn để làm là nhận ra rằng những gì bạn đang cố gắng làm là đơn giản về cơ bản, và do đó <$> không phải là công cụ thích hợp cho công việc. Bạn đang cho ăn kết quả của một hành động khác; mà về bản chất yêu cầu bạn sử dụng Monad.

Các vấn đề liên quan