Sunday, September 26, 2010

View patterns for validation

GHC's ViewPatterns extension has a lot of unexpected uses. One that I've found recently is input validation.

{-# LANGUAGE ViewPatterns #-}

import Control.Monad
import Text.Printf

months :: [String]
months = words "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"

Using this function:

range :: (Ord a) => a -> a -> a -> a
range lb ub x
| (x < lb) || (x > ub) = error "argument out of range"
| otherwise = x

we can put bounds on arguments:

month :: Int -> String
month (range 1 12 -> m) = months !! (m-1)

like so:

GHCi> month 3
"Mar"
GHCi> month 15
"*** Exception: argument out of range

This version provides better error messages:

-- V for 'verbose'
rangeV :: (Ord a, Show a) => String -> a -> a -> a -> a
rangeV fun lb ub x
| (x < lb) || (x > ub) = error (printf msg fun (show x) (show lb) (show ub))
| otherwise = x
where msg = "%s: argument %s is outside of range [%s,%s]"

like so:

monthV :: Int -> String
monthV (rangeV "monthV" 1 12 -> m) = months !! (m-1)

GHCi> monthV 3 "Mar" GHCi> monthV 15 "*** Exception: monthV: argument 15 is outside of range [1,12]

Or handle failure monadically:

-- Mp for MonadPlus
rangeMp :: (MonadPlus m, Ord a) => a -> a -> a -> m a
rangeMp lb ub x = guard ((x >= lb) && (x <= ub)) >> return x

like so:

monthMaybe :: Int -> Maybe String
monthMaybe (rangeMp 1 12 -> Just m) = Just (months !! (m-1))
monthMaybe _ = Nothing

GHCi> monthMaybe 3 Just "Mar" GHCi> monthMaybe 15 Nothing

12 comments:

  1. Yeah, that's nice. Personally I prefer the latter and would prefer it with Either so that you get actual information about why it failed.

    > {-# LANGUAGE ViewPatterns #-}

    Supposing we use the Control.Applicative.Error class.

    > import Control.Applicative.Error

    And we define a function that ensures X is true:

    > ensure :: String -> (a -> Bool) -> a -> Failing a
    > ensure err p a
    > | p a = Success a
    > | otherwise = Failure [err]

    We can then define inRange in terms of it for Failing:

    > inRange :: (Show a,Ord a) => a -> a -> a -> Failing a
    > inRange lb ub x = ensure err (\a -> a>=lb && a<=ub) x where
    > err = "Must be in range >=" ++ show lb ++ " and <= " ++ show ub

    And our `month' and `day' functions are defined like this:

    > months :: [String]
    > months = words "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"

    > month :: Int -> Failing String
    > month (inRange 1 12 -> Success m) = Success $ months !! (m-1)
    > month _ = Failure ["Month must be in range 1-12."]

    > days :: [String]
    > days = words "Monday Tuesday Wednesday Thursday Friday Saturday Sunday"

    > day :: Int -> Failing String
    > day (inRange 1 7 -> Success d) = Success $ days !! (d-1)
    > day _ = Failure ["Day must be in range 1-7."]

    The nice thing is we get this for free:

    λ> (,) <$> month 1 <*> day 3
    Success ("Jan","Wednesday")

    λ> (,) <$> month 1 <*> day 12
    Failure ["Day must be in range 1-7."]

    λ> (,) <$> month 24 <*> day 12
    Failure ["Month must be in range 1-12.","Day must be in range 1-7."]

    It kind of sucks to be building strings all the time, it'd be
    nicer to have proper types to represent these invariants, e.g.

    > data Prop a = OutOfRange a | Empty a | WrongSize a | InvalidFormat a | Ok a

    Or something like that. I suppose throwing exceptions is faster
    than creating intermediate data structures.

    ReplyDelete
  2. Nice idea! But I would like to see listed in your post the alternative without view patterns:

    month :: Int -> String
    month i | inRange 1 12 i = months !! (i-1)
    | otherwise = error "..."

    inRange :: Ord a => a -> a -> a -> Bool
    inRange lb ub x = x >= lb && x <= ub

    (the blog will probably clobber indentation)

    ReplyDelete
  3. Thanks for the suggestions Chris, and for mentioning Control.Applicative.Error. It definitely includes a few things I reimplemented in a recent project; I'll want to switch over.

    ReplyDelete
  4. This is one of the things I love about (GHC) Haskell: you can often simply create a clever new "pseudo-syntax" out of the blue!!

    This "range checking pattern" would be rigid syntax in most other languages. Here it's just something that emerges from the programming paradigm.

    ReplyDelete


  5. شركة مكافحة حشرات بالدمام 0590352700 ديكوريند
    شركة مكافحة حشرات بالدمام

    مكافحة الحشرات من الأشياء الواجب فعلها علي اتم وجه حتي نتجنب الامراض و الأوبه التي تسببها الحشرات .

    و لحل مثل هذه المشكله لابد من اللجوء الي الشركات ذوي الخبره ، علي أن تكون متميزه في مكافحة الحشرات و طرق القضاء عليها .

    و مما لا شك فيه أن أفضل شركة بالدمام هي شركة ديكوريند لمكافحة الحشرات .

    لما تقدمه الشركه من خدمات و طرق فعاله للقضاء علي الحشرات بجميع أنواعها .

    الشركه تعتمد علي العماله المدربه جيدا ، و المبيدات و الأجهزه الفعاله في القضاء علي مثل هذه الآفات الضاره.
    شركة مكافحة حشرات بالدمام
    شركة مكافحة حشرات بالدمام
    أنواع الحشرات :

    نتيجة لارتفاع درجة الحراره و نسبه الرطوبة في الدول العربيه ، تنتشر و تكثر الحشرات في كثير من المناطق و خاصة التي تطل علي المسطحات المائيه .

    تؤدي تلك الآفات الضاره الي اصابه قاطني المنزل بالأمراض و شعورهم بعدم الراحه ، و تقوم أيضا بإتلاف المنتجات الغذائيه و الاثاث و الملابس .

    و تتنوع انواع الحشرات كالآتي :

    ذباب
    بعوض
    براغيث
    نمل
    صراصير
    و غيرها الكثير

    و لتنجب مثل هذه المشاكل و الأمراض و الأوبئه و المخاطر التي قد تصيب أطفالنا الصغار ، يجب القضاء علي مثل هذه الحشرات تماما و ذلك بالاستعانه بذوي الخبره .

    شركة ديكوريند تتمتع بالخبره الكافيه للقضاء علي الحشرات بجميع أنواعها و أشكالها بإستخدام المعدات الجيده و العماله المدربه و المبيدات المتميزه .
    لماذا شركة ديكوريند ؟

    مما لاشك فيه أن أسعار شركة ديكوريند لا تقبل المنافسه اطلاقا ، لما تقدمه الشركه من خدمات جيده جدا في مقابل الأسعار التي تكون في متناول الجميع .

    و تسعي الشركه دائما إلي تقديم أرقي و أفضل الخدمات ، التي ترضي جميع عملائنا الكرام ، فلا تتردد في الاتصال بنا .

    اتصل الان : 0556043168 – 0590352700 شركة ديكوريند لمكافحة الحشرات بالدمام و جميع مناطق المملكة العربيه السعودية

    من خدماتنا :

    تنظيف فلل بالرياض

    تنظيف موكيت بالرياض

    تنظيف منازل بالرياض

    تنظيف شقق بالرياض

    تنظيف خزانات بالرياض

    تنظيف مجالس بالرياض

    ReplyDelete
  6. يتم استخدام بعض طرق إبعاد الحمام مثل وضع الشباك و رفع الأسلاك الحديدية الصلبة ومنع الحمام من الوقوف في الحقل وتعتبر هذه الطريقة فعالة في طرد الحمام الموجود فوق اللوحات الإعلانية وكذلك تلك المعلقة على جدران المؤسسات والشركات الاعلانات التجارية. الشرائط ذات الألوان الزاهية يمكن استخدام الشرائط ذات اللون الأحمر أو الأصفر الفاتح لصد الحمام عن طريق لصق الحمام الذي يبلغ طوله نصف متر على جميع النوافذ أو على هذه النوافذ حيث يوجد الحمام. يمكن تحقيق طريقة العصا أو المسبحة هذه باستخدام خيط مسبحة طويل أو خيط مسبحة.لف الخيط حول الحمام ، مثل النافذة أو البلاط أو المنزل على السطح ، لتحقيق نتائج فعالة ، أكثر من سلسلة ، ويمكنك ربط ربطة عنق أو شريط على السلك ، الحمام طيور ذكية ، فإن ضربه بسلك محكم سيؤذيه ولن يعود ، بالإضافة إلى أن السلك المعلق سيخيفه.
    شركة تركيب طارد الحمام بالمدينة المنورة
    اسباب انتشار الحشرات
    عدم الاهتمام بنظافة داخل المنزل وخارجه فلا بد من معرفة أن القمامة والربيع من أكبر العوامل المؤدية لوجود الحشرات وانتشارها.
    تنبعث رائحة كريهة من المنزل بسبب تلف الأشياء أو القمامة القديمة أو التقشير المتروك لفترة طويلة.
    فضح الطعام أو بقايا الطعام في المنزل لفترة طويلة.
    إخراج الفاكهة من الثلاجة سيجذب الحشرات وخاصة الذباب.
    توجد مياه راكدة في الإناء أو الأرضية لأن المياه الراكدة تنبعث منها رائحة تساعد على جلب الحشرات.
    توجد حيوانات في المنزل ، خاصة إذا كانت مهملة ولا تنظف ولا ترش
    الحشرات السامةا.
    الحشرات من أكثر المخلوقات المزعجة التي يمكن أن تجدها في منزلك ، فهذه الحشرات يمكن أن تسبب العديد من الأمراض المختلفة.
    شركة مكافحة حشرات بالمدينة المنورة
    غالبًا ما تحتاج الأماكن الأكثر استخدامًا والأوساخ في المنزل إلى تنظيف الرخام والأرضيات والجدران وإزالة الزيت عن الجدران والأرضيات وتنظيف المطبخ سواء كان من الألمنيوم أو الخشب وتلميعه. ضعها وأعدها بأمان ، ويقوم الفريق بتنظيف الحوائط بمنظفات خاصة ، سواء كانت رخامية أو سيراميك ، أرضيات وأحواض مختلفة ، لإزالة الدهون والبقع والزيوت والأتربة ، وتلميعها جيدًا لجعلها تبدو جديدة كما هي ، مع منعها تلف
    شركة تنظيف منازل بالمدينة المنورة

    ReplyDelete
  7. Gutt Websäit : Biodata
    Gutt Websäit : Zonahobisaya
    Gutt Websäit : Zonahobisaya
    Gutt Websäit : Zonahobisaya
    Gutt Websäit : Zonahobisaya
    Gutt Websäit : Zonahobisaya
    Gutt Websäit : Zonahobisaya
    Gutt Websäit : Zonahobisaya

    ReplyDelete
  8. It is imperative that we read blog post very carefully. I am already done it and find that this post is really amazing.

    ReplyDelete