I want the following outcome:
"1" -> true
"0" -> false
nil -> nil
How can this function be improved?
#(when-not (nil? %) (if % "1" "0"))
Your requirement translates directly into a map and maps are also callable as functions in Clojure, so {1 true, 0 false} is the function you want.
({1 true, 0 false} 1) ;;=> true
({1 true, 0 false} 0) ;;=> false
({1 true, 0 false} nil) ;;=> nil
Related
defmodule My do
def go do
x = nil
if(not x) do
IO.puts "hello"
else
IO.puts "goodbye"
end
end
end
In iex:
/elixir_programs$ iex c.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> My.go
** (ArgumentError) argument error
c.exs:5: My.go/0
iex(1)>
According to Programming Elixir >= 1.6, p.35:
Elixir has three special values related to Boolean operations: true,
false, and nil. nil is treated as false in Boolean contexts.
It doesn't seem to be true:
defmodule My do
def go do
x = false
if (not x) do
IO.puts "hello"
else
IO.puts "goodbye"
end
end
end
In iex:
~/elixir_programs$ iex c.exs
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> My.go
hello
:ok
iex(2)>
#spec not true :: false
#spec not false :: true
def not value do
:erlang.not(value)
end
Elixir 's latest definition of not function shows that it only receive false and true.
However, nil is not belongs to them, so it shows argument error.
Elixir has three special values related to Boolean operations: true, false, and nil. nil is treated as false in Boolean contexts.
nil is just an atom, that is nil === :nil.
You can consider using ! operator, which in fact is Kernel.! macro.
Receives any argument (not just booleans) and returns true if the
argument is false or nil; returns false otherwise.
!nil will return true.
"Kernel.not/1" or not/1 expects a Boolean value
note: Other values different of nil and false are true
try this example:
x = nil
if (x) do true else false end
false
Examples with short-if condition and true, false, nil values
iex> if nil , do: true, else: false
false
iex> if !nil , do: true, else: false
true
iex> if false , do: true, else: false
false
iex> if !false , do: true, else: false
true
iex> if not false , do: true, else: false
true
iex> if true , do: true, else: false
true
iex> if !true , do: true, else: false
false
iex> if not true , do: true, else: false
false
I would like to be able to generate user-friendly or specify custom error messages for validation errors in these schemas:
(def Uuid (s/constrained String #(re-matches #"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$" (name %))))
(def FirstName s/Str)
(def LastName s/Str)
(s/defschema Person {(s/required-key :id) Uuid,
(s/required-key :first-name) FirstName,
(s/required-key :last-name) LastName})
Valid schema:
{
:uuid "e143499c-1257-41e4-b951-c9e586994ff9"
:first-name "john"
:last-name "smith"
}
Invalid schema:
{
:uuid ""
:first-name nil
:last-name nil
}
Invalid schema - Errors:
{
"id" : "(not (app.person/fn--4881 \"\"))",
"first-name" : "(not (instance? java.lang.String nil))"
"last-name" : "(not (instance? java.lang.String nil))"
}
I would like to be able to generate something a bit more readable to non-programmers, for example:
{
"id" : "invalid uuid",
"first-name" : "must be a string"
"last-name" : "must be a string"
}
Funnily exactly this was released as a library a few days ago.
See:
https://github.com/siilisolutions/humanize
First you also need to tag your Uuid schema so you can match it later on:
;; Note the last param I added:
(def Uuid (sc/constrained
String
#(re-matches #"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
(name %))
'UUID))
(require '[schema.core :as sc]
'[humanize.schema :as hs])
(#'hs/explain (sc/check Person {:id "foo"
:first-name "foo"
:last-name 3})
(fn [x]
(clojure.core.match/match
x
['not ['UUID xx]]
(str xx " is not a valid UUID")
:else x)))
Results in:
=> {:id "foo is not a valid UUID", :last-name "'3' is not a string but it should be."}
Note, it needed a little trick since hs/explain is private unfortunately.
Consider the following spec for a text or a link layer port number:
(require '[clojure.spec.alpha :as spec])
(spec/def ::text (spec/and string? not-empty))
(spec/valid? ::text "a") ; => true
(spec/valid? ::text "") ; => false
(spec/def ::port (spec/and pos-int? (partial > 65535)))
(spec/valid? ::port 4) ; => true
(spec/valid? ::port 0) ; => false
(spec/def ::text-or-port (spec/or ::text ::port))
(spec/valid? ::text-or-port 5) ; => true
(spec/valid? ::text-or-port "hi") ; => false
For some reason it only accepts port-numbers and not text, why would that be?
The key to understanding this problem can be found in in the documentation and using spec/conform.
(spec/conform ::text-or-port 5)
; => [:user/text 5]
The problem is that clojure.spec.alpha/or has an API which is dissimmilar to clojure.core/or which given two arguments returns the first truthy one:
(#(or (string? %) (integer? %)) 5) ; => true
(#(or (string? %) (integer? %)) "") ; => true
(#(or (string? %) (integer? %)) :a) ; => false
Rather it takes pairs of labels and specs/predicates. And since even namespaced keywords are accepted as labels the ::text-or-port spec given in the OP matched only that which passed the requirements for ::port and gave it the label ::text. Below is a correct spec for that which we want to match:
(spec/def ::text-or-port (spec/or :text ::text
:port ::port))
(spec/valid? ::text-or-port "hi") ; => true
(spec/valid? ::text-or-port 10) ; => true
I couldn't understand differences between IFn and fn.
Could you give examples too?
Please provide differences between those functions:
(fn? x)
(ifn? x)
they behave like same.
Clojure docs describes the difference quite clearly:
(fn? x)
Returns true if x implements Fn, i.e. is an object created via fn.
(ifn? x)
Returns true if x implements IFn. Note that many data structures
(e.g. sets and maps) implement IFn
You can test it:
(fn? (fn [] nil)) ;; => true
(fn? #{}) ;; => false
(fn? {}) ;; => false
(fn? []) ;; => false
(fn? :a) ;; => false
(fn? 'a) ;; => false
(ifn? (fn [] nil)) ;; => true
(ifn? #{}) ;; => true
(ifn? {}) ;; => true
(ifn? []) ;; => true
(ifn? :a) ;; => true
(ifn? 'a) ;; => true
In other words fn? says if its argument is an object which is just and only a function (created with (fn ...)), ifn? says if an object is a thing which can be called like a function (even if it wasn't created with (fn ...)).
How i can count mobile and web access discarding a nil values from a list of maps? the output should be anything like this " Statistic mobile = 1 web = 2", but all is imutable on other languagens a simple i++ resolve but how is in clojure. thanks.
def data [{:name "app1" :type "mobile" }
{:name "site1" :type "web" }
{:name "site1" :type "web" }
{:name "boot" :type nil }]
(frequencies (map :type data))
gives
{"mobile" 1, "web" 2, nil 1}
user=> (for [[k v] (group-by :type data) :when k] [k (count v)])
(["mobile" 1] ["web" 2])