How do you replace static assertions with prfuns? - ats

Consider this unrefined (but working) program:
#include "share/atspre_staload.hats"
datatype class =
| mage | fighter | thief | cleric
| wizard | warrior | ninja | priest
fn promoteclass(job: class): class =
case- job of
| mage() => wizard()
| fighter() => warrior()
| thief() => ninja()
| cleric() => priest()
fn getsomeclass(): class = mage()
val- wizard() = promoteclass(getsomeclass())
implement main0() = ()
it's a runtime error to pass wizard() to promoteclass(), and it's a runtime error if promoteclass(getsomeclass()) is changed to return something other than a wizard().
Which is no good! I'd much rather flip both of those - signs to + and get compile time errors in both of the previous two error cases. It'd also be nice if it could be a compile-time error to accidentally transpose a promotion case, to say priest() => cleric()
This desire led a refinement of the above, which also works just fine:
#include "share/atspre_staload.hats"
datatype class(int) =
| mage(0) | fighter(1) | thief(2) | cleric(3)
| wizard(4) | warrior(5) | ninja(6) | priest(7)
fn promoteclass{n:int | n < 4}(job: class(n)): [m:int | m == n + 4] class(m) =
case+ job of
| mage() => wizard()
| fighter() => warrior()
| thief() => ninja()
| cleric() => priest()
fn getsomeclass(): class(0) = mage()
val+ wizard() = promoteclass(getsomeclass())
implement main0() = ()
But what I'd like to do is replace the the n < 4 and such above with dataprops and proof functions. Is that possible? Mainly I want to do that to better understand theorem-proving in ATS, but it also seems that this is the path to getting the same guarantees as the second example without all of its verbosity (especially as additional functions are added, that operate on these classes).
This is what I tried to do:
#include "share/atspre_staload.hats"
datatype class(int) =
| mage(0) | fighter(1) | thief(2) | cleric(3)
| wizard(4) | warrior(5) | ninja(6) | priest(7)
dataprop promotable(int) =
| {n:int}promotable_yes(n)
| {n:int}promotable_no(n)
prfun test_promotable.<>.{n:int}():<> promotable(n) =
sif n < 4 then promotable_yes{n}() else promotable_no{n}()
fn promoteclass{n:int}(job: class(n)): [m:int] class(m) =
let
prval promotable_yes() = test_promotable{n}()
in
case+ job of
| mage() => wizard()
| fighter() => warrior()
| thief() => ninja()
| cleric() => priest()
end
fn getsomeclass(): class(0) = mage()
val+ wizard() = promoteclass(getsomeclass())
implement main0() = ()
But right away I'm told that the prval assignment is non-exhaustive.

The following code should fix the erasure-error:
fn promoteclass{n:int}
(pf: promotable(n) | job: class(n)): [m:int] class(m) =
(
case+ job of
| mage() => wizard()
| fighter() => warrior()
| thief() => ninja()
| cleric() => priest()
| _ =/=>> () where
{
prval () =
(
case+ pf of
| pf_mage() => ()
| pf_fighter() => ()
| pf_thief() => ()
| pf_cleric() => ()
) : [false] void
}
)
You can move the proof code into a proof function; the type for the proof function is a bit complex. Here is what I have:
prfn
not_promotable
{n:int | n != 0&&n != 1&&n != 2&&n != 3 }
(pf: promotable(n)):<> [false] void =
(
case+ pf of
| pf_mage() => ()
| pf_fighter() => ()
| pf_thief() => ()
| pf_cleric() => ()
)
fn promoteclass{n:int}
(pf: promotable(n) | job: class(n)): [m:int] class(m) =
(
case+ job of
| mage() => wizard()
| fighter() => warrior()
| thief() => ninja()
| cleric() => priest()
| _ =/=>> () where { prval () = not_promotable(pf) }
)

The error you got says that test_promotable may return promotable_no, which is true (as the test may fail).
Not sure whether the following style is what you want:
dataprop
promotable(int) =
| pf_mage(0)
| pf_fighter(1)
| pf_thief(2)
| pf_cleric(3)
fn promoteclass{n:int}
(pf: promotable(n) | job: class(n)): [m:int] class(m) =
(
case+ job of
| mage() => wizard()
| fighter() => warrior()
| thief() => ninja()
| cleric() => priest()
| _ =/=>>
(
case+ pf of
| pf_mage() => ()
| pf_fighter() => ()
| pf_thief() => ()
| pf_cleric() => ()
)
)

Related

Laravel query builder: group relations and count total amount

I'm using Laravel 9 and want to create chart for user-role dependency and I need count amount of users with specific role
This is my tables (for example)
// users
| id | name |
| --- | --- |
| 1 | John Doe |
| 2 | Not John Doe |
// roles
| id | slug |
| --- | ----- |
| 1 | admin |
| 2 | ga |
// role_users
| user_id | role_id |
| ------- | ------- |
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
I want to count users with admin role
User::whereHas('roles', fn ($query) => $query->whereSlug(Role::ADMIN()))->count()
It works, I get correct number but I need to do same query for another roles so this is where things become ugly. I want to avoid doing this
$a = User::whereHas('roles', fn ($query) => $query->whereSlug(Role::ADMIN()))->count(); // 2
$b = User::whereHas('roles', fn ($query) => $query->whereSlug(Role::MODERATOR()))->count(); // 10
$c = User::whereHas('roles', fn ($query) => $query->whereSlug(Role::GA()))->count(); // 12
$d = User::whereHas('roles', fn ($query) => $query->whereSlug(Role::CA()))->count(); // 85
At the end I need to get an array of numbers like [2, 10, 12, 85]. How can I do it using Laravel queries?
Was able to achieve similar with
$users_count = DB::table('role_users')
->select(DB::raw('count(*) as users_count, role_id'))
->groupBy('role_id')
->get()
->mapWithKeys(fn($item, $key) => [$item->role_id => $item->users_count])
->toArray();
Now I can access count array with array_values($users_count)

Optimal algorithm to determine relationship tree

Suppose that you have a list of formulas like the following one:
KPI1 = somevalue1 + somevalue2
KPI2 = somevalue1 + somevalue3
KPI3 = KPI1 + somevalue4
KPI4 = KPI2 + KPI3
etc.
Which is the optimal algorithm that can be used to obtain a relationship tree for each of the elements referenced in the formulas?
i.e., using the example above:
+------------------------------------------------------------+
| somevalue3 somevalue1 somevalue2 somevalue4 |
| | | | | | |
| --------------- -------------- | |
| | | | |
| KPI2 KPI1 | |
| | | | |
| | --------------------- |
| | | |
| | KPI3 |
| | | |
| --------------------------- |
| | |
| KPI4 |
+------------------------------------------------------------+
You can use a hashmap where a key corresponds to a defined name (e.g. "KPI3"), and the corresponding value is a list of names/values on which that name depends (e.g. ["KPI1", somevalue4]).
Here is an implementation in JavaScript, where we can use the native Map constructor. Once the map is populated with all the dependencies, a recursive function can for example print the tree:
function printTree(map, name, indent="") {
console.log(indent + name);
let children = map.get(name);
if (children !== undefined) {
for (let childName of children) {
printTree(map, childName, indent+" ");
}
}
}
let map = new Map();
map.set("KPI1", ["value1", "value2"]);
map.set("KPI2", ["value1", "value3"]);
map.set("KPI3", ["KPI1", "value4"]);
map.set("KPI4", ["KPI2", "KPI3"]);
printTree(map, "KPI4");
In Python you would use a dictionary:
def print_tree(d, name, indent=""):
print(indent + name)
children = d.get(name, None)
if children is not None:
for childName in children:
print_tree(d, childName, indent+" ")
d = dict()
d["KPI1"] = ["value1", "value2"]
d["KPI2"] = ["value1", "value3"]
d["KPI3"] = ["KPI1", "value4"]
d["KPI4"] = ["KPI2", "KPI3"]
print_tree(d, "KPI4")

Performance of whereHas laravel

I have read many articles warning about whereHas(it use subquery in where exists) performance when the data is big(Here: 18415, 5328, ...). In my case below will I need to replace it? And how?.
Table products
+----+---------------------+
| id | created_at |
+----+---------------------+
| 1 | 2020-10-10 10:10:10 |
| 2 | 2020-10-10 10:10:10 |
+----+---------------------+
Table product_translations(indexes: map_id, language_code, slug)
+----+--------+---------------+-----------+-----------+
| id | map_id | language_code | name | slug |
+----+--------+---------------+-----------+-----------+
| 1 | 1 | en | name en 1 | name-en-1 |
| 2 | 1 | es | name es 1 | name-es-1 |
| 3 | 2 | en | name en 2 | name-en-2 |
| 4 | 2 | es | name es 2 | name-es-2 |
+----+--------+---------------+-----------+-----------+
Product.php
function translation(){
return $this->hasOne('App\Models\ProductTranslation', 'map_id', 'id');
}
ProductController.php
function view($slug){
$currentItem = Product::with(['translation' => function($q) use($slug){
$q->where(['slug' => $slug, 'language_code' => \App::getLocale()]);
}])->whereHas('translation', function($q) use ($slug){
$q->where(['slug' => $slug, 'language_code' => \App::getLocale()]);
})
if ($currentItem == null) {
abort(404);
}
return view('product.view', compact('currentItem'));
}
I'm using laravel 8x.
You can replace whereHas with whereIn.
$currentItem = Product::with(['translation' => function($q) use($slug){
$q->where(['slug' => $slug, 'language_code' => \App::getLocale()]);
}])->whereIn(
'id',
ProductTranslation::select('map_id')
->where(['slug' => $slug, 'language_code' => \App::getLocale()])
);
Select the foreign key from a subselect and uses it with where in.

Yii2 AR left join with where and relations

Book
----------------------------
| id | name | published |
----------------------------
| 1 | book1 | 1 |
| 2 | book2 | 1 |
| 3 | book3 | 0 |
Chapter
----------------------------
| id | book_id | name | published |
----------------------------
| 1 | 1 | chapter1 | 1 |
| 2 | 1 | chapter2 | 0 |
| 2 | 2 | chapter1 | 0 |
| 3 | 3 | chapter1 | 1 |
class Book{
public function getChapter()
{
return $this->hasMany(Chapter::className(), ['kook_id' => 'id']);
} }
class Chapter{
public function getBook()
{
return $this->hasOne(Book::className(), ['id' => 'book_id']);
} }
How can i get published books with published pages using ActiveRecord (i want get book1 with chapter1 and book2 without any chapters)?
smth like Book::find($id)->where(['published' => 1])-> {{{left join with where}}} ->all())
ADDED
And then i wand to use it
echo $book->name;
foreach($book->getChapter() as chapter){
echo chapter->name;
}
Change your Book class relation as
class Book
{
public function getChapter()
{
return $this->hasMany(Chapter::className(), ['book_id' => 'id'])->where(['published' => 1]);
}
}
To get Related Records use with()
Book::find()->with(['chapter'])->where(['id' => $id ,'published' => 1])->all()
To Use it:
//Book Name
echo $book->name;
//For Chapters Name
if($book->chapter){
foreach($book->chapter as $chapter){
echo $chapter->name;
}
}

LINQ - How to select with subquery

My data is:
+-----+--------+-----------+
| Id | Name | ParentId |
+-----+--------+-----------+
| 1 | A | 0 |
| 2 | B | 0 |
| 3 | C | 1 |
+-----+--------+-----------+
How do I get C if we have ParentId = 0 by LINQ?
Assuming "Categories" is the collection name, I think this is what you need.
var parentCategoryIds = Collections.Where(c => c.ParentId == 0).Select(c => c.Id);
var results = Collections.Where(c => parentCategoryIds.Contains(c.ParentId));
And if you want to get the name, you can do
var names = results.Select(r => r.Name);
These three statements could be written into one linq statement but I did this for clarity.

Resources