Niedawno wpadłem na sytuacje, w których muszę przekazać funkcję predykatu do innej funkcji, i dość często logika, której szukam, to w zasadzie „czy ta wartość pasuje do tego wzorca?”
Dopasowywanie wzorców wydaje się być preferowane w deklaracjach, do
blokach i opisach list, ale istnieje wiele funkcji, które przyjmują predykaty a -> Bool
, w których bardzo przydatne byłoby jakoś przekazać wzorzec. Na przykład takeWhile
, until
, find
, span
, itd.
Do tej pory robiłem \a -> case a of MyCons _ -> True; otherwise -> False
lub pisałem nazwaną funkcję a la, let myPred (MyCons _) = True; myPred _ = False in
ale oba wydają się strasznie brzydkie i niezbyt idiomatyczne. „Oczywisty” (i zły) sposób byłby czymś podobnym, \(MyCons _) -> True
ale generuje to błąd, ponieważ jest stronniczy, oczywiście, i nawet wtedy wydaje się, że musi istnieć czystszy sposób.
Czy istnieje bardziej zwięzły / czysty sposób na zrobienie czegoś takiego? Czy też chodzi o rzeczy całkowicie niewłaściwe?
let
klauzuli, której nie lubisz - chociaż wolęwhere
klauzulę równoważną , więc nie zaśmieca to głównej definicji. Oczywiście, jeśli będziesz potrzebować tego narzędzia więcej niż jeden raz, zdefiniuj je jako funkcję najwyższego poziomu.let myPred...
styl jest zły , ale wydaje się bardziej gadatliwy, niż oczekiwałbym bardzo prostego pomysłu, co prowadzi mnie do zastanowienia się, czy nie szczekam na niewłaściwym drzewie.maybe :: b -> (a -> b) -> Maybe a -> b
ibool :: a -> a -> Bool -> a
, a następnie używać go z funkcji (y) jako argument (y) Boolean produkujących. np.myCons z f (MyCons x) = f x ; myCons z f _ = z
następnie zadzwońmyCons False (const True) aMyConsValue
. jest to prawie to, co napisałeś, ma tylko jeden poziom „pośredni” / „abstrakcyjny” za pomocą funkcjonalnych argumentów, wrzucony w to.Odpowiedzi:
Możesz użyć rozszerzenia językowego LambdaCase
\case MyCons _ -> True; _ -> False
, ale nie oszczędza to tylu znaków.Wierzę, że możesz napisać serię funkcji
constructedWith :: (Generic a) => (b -> a) -> a -> Bool
,constructedWith2 :: (Generic a) => (b -> c -> a) -> a -> Bool
ale nie jestem wystarczająco kompetentny w Generics, aby wdrożyć go bez kilku godzin testowania. Spróbuję tego i zredaguję swoją odpowiedź, jeśli uda mi się to rozgryźć lub jeśli to ślepy zaułek.EDYCJA: Tak, możesz to zrobić! Oto link do mojego kodu, który implementuje to wszystko od zera:
https://repl.it/@lalaithion/ConstructedWith
Jednak użycie czegoś takiego jak http://hackage.haskell.org/package/generic-deriving-1.13.1/docs/Generics-Deriving-ConNames.html dla wszystkich ogólnych kodów instalacyjnych może być lepsze.
źródło